Changeset 4132
- Timestamp:
- 02/21/08 15:21:21 (5 months ago)
- Files:
-
- branches/1.0/turbogears/database.py (modified) (15 diffs)
- branches/1.0/turbogears/tests/test_paginate.py (modified) (1 diff)
- branches/1.0/turbogears/tests/test_sqlalchemy.py (modified) (9 diffs)
- branches/1.0/turbogears/testutil.py (modified) (15 diffs)
- branches/1.1/turbogears/database.py (modified) (12 diffs)
- branches/1.1/turbogears/tests/test_paginate.py (modified) (1 diff)
- branches/1.1/turbogears/tests/test_sqlalchemy.py (modified) (8 diffs)
- branches/1.1/turbogears/testutil.py (modified) (15 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
branches/1.0/turbogears/database.py
r4129 r4132 1 """Provides convenient access to an SQLObject or SQLAlchemy 2 managed database.""" 1 """Convenient access to an SQLObject or SQLAlchemy managed database.""" 3 2 4 3 import sys … … 150 149 151 150 def _is_interesting_version(self): 152 " Return True only if version of MySQLdb <= 1.0."151 """Return True only if version of MySQLdb <= 1.0.""" 153 152 import MySQLdb 154 153 module_version = MySQLdb.version_info[0:2] … … 201 200 202 201 def begin(self, conn=None): 203 " Starts a transaction."202 """Start a transaction.""" 204 203 if not self.supports_transactions: 205 204 return conn … … 216 215 217 216 def commit(self): 218 " Commits the current transaction."217 """Commit the current transaction.""" 219 218 if not self.supports_transactions: 220 219 return … … 227 226 228 227 def rollback(self): 229 " Rolls back the current transaction."228 """Rollback the current transaction.""" 230 229 if not self.supports_transactions: 231 230 return … … 238 237 239 238 def end(self): 240 " Ends the transaction, returning to a standard connection."239 """End the transaction, returning to a standard connection.""" 241 240 if not self.supports_transactions: 242 241 return … … 323 322 324 323 def commit_all(): 325 " Commits the Transactions in all registered hubs (for this thread)"324 """Commits the transactions in all registered hubs (for this thread).""" 326 325 for hub in hub_registry: 327 326 hub.commit() 328 327 329 328 def rollback_all(): 330 " Rolls back the Transactions in all registered hubs (for this thread)"329 """Rollback the transactions in all registered hubs (for this thread).""" 331 330 for hub in hub_registry: 332 331 hub.rollback() 333 332 334 333 def end_all(): 335 " Ends the Transactions in all registered hubs (for this thread)"334 """End the transactions in all registered hubs (for this thread).""" 336 335 for hub in hub_registry: 337 336 hub.end() … … 349 348 return _engine is not None 350 349 351 [run_with_transaction.when("not _use_sa(args)")] # include "args" to avoid call being pre-cached 350 # include "args" to avoid call being pre-cached 351 [run_with_transaction.when("not _use_sa(args)")] 352 352 def so_rwt(func, *args, **kw): 353 353 log.debug("Starting SQLObject transaction") … … 364 364 raise 365 365 except: 366 # No need to "rollback" the sqlalchemy unit of work, because nothing367 # has hit the db yet.366 # No need to "rollback" the sqlalchemy unit of work, 367 # because nothing has hit the db yet. 368 368 rollback_all() 369 369 raise … … 371 371 end_all() 372 372 373 [restart_transaction.when("not _use_sa(args)")] # include "args" to avoid call being pre-cached 373 # include "args" to avoid call being pre-cached 374 [restart_transaction.when("not _use_sa(args)")] 374 375 def so_restart_transaction(args): 375 376 #log.debug("ReStarting SQLObject transaction") … … 388 389 output = dispatch_error( 389 390 controller, real_func, None, exception, *args, **kw) 390 391 391 except dispatch.NoApplicableMethods: 392 392 raise exc_type, exc_value, exc_trace 393 394 393 else: 395 394 del exc_trace … … 400 399 def sa_rwt(func, *args, **kw): 401 400 log.debug("Starting SA transaction") 402 req = cherrypy.request403 req .sa_transaction = make_sa_transaction(session)401 request = cherrypy.request 402 request.sa_transaction = make_sa_transaction(session) 404 403 try: 405 404 try: 406 405 retval = func(*args, **kw) 407 406 except (cherrypy.HTTPRedirect, cherrypy.InternalRedirect): 408 log.debug('Redirect in transaction - will commit now')409 407 # If a redirect happens, commit and proceed with redirect 410 if sa_tr_active(req.sa_transaction): 411 req.sa_transaction.commit() 408 if sa_transaction_active(request.sa_transaction): 409 log.debug('Redirect in active transaction - will commit now') 410 if hasattr(session, 'commit'): 411 session.commit() 412 else: # SA < 0.4 413 request.sa_transaction.commit() 414 else: 415 log.debug('Redirect in inactive transaction') 412 416 raise 413 417 except: 414 log.debug('Error in transaction - will rollback now')415 418 # If any other exception happens, rollback and re-raise error 416 if sa_tr_active(req.sa_transaction): 417 req.sa_transaction.rollback() 419 if sa_transaction_active(request.sa_transaction): 420 log.debug('Error in active transaction - will rollback now') 421 if hasattr(session, 'rollback'): 422 session.rollback() 423 else: # SA < 0.4 424 request.sa_transaction.rollback() 425 else: 426 log.debug('Error in inactive transaction') 418 427 raise 419 428 # If the call was successful, commit and proceed 420 if sa_tr_active(req.sa_transaction): 421 log.debug('Successful transaction - will commit now') 422 req.sa_transaction.commit() 429 if sa_transaction_active(request.sa_transaction): 430 log.debug('Transaction is still active - will commit now') 431 if hasattr(session, 'commit'): 432 session.commit() 433 else: # SA < 0.4 434 request.sa_transaction.commit() 435 else: 436 log.debug('Transaction is already inactive') 423 437 finally: 424 438 log.debug('Ending SA transaction') 425 req.sa_transaction.close()439 session.close() 426 440 return retval 427 441 … … 430 444 def sa_restart_transaction(args): 431 445 log.debug("Restarting SA transaction") 432 req = cherrypy.request 433 if sa_tr_active(req.sa_transaction): 434 req.sa_transaction.rollback() 435 session.clear() 436 req.sa_transaction = make_sa_transaction(session) 437 438 def sa_tr_active(tr): 439 if hasattr(session, 'context'): 440 # SA 0.3 441 return tr.session.transaction 446 request = cherrypy.request 447 if sa_transaction_active(request.sa_transaction): 448 log.debug('Transaction is still active - will rollback now') 449 if hasattr(session, 'rollback'): 450 session.rollback() 451 else: # SA < 0.4 452 request.sa_transaction.rollback() 442 453 else: 443 # SA 0.4 can be treated as always active; if no transaction444 # is active, commit and rollback quietly do nothing445 return True454 log.debug('Transaction is already inactive') 455 session.close() 456 request.sa_transaction = make_sa_transaction(session) 446 457 447 458 def make_sa_transaction(session): 448 if hasattr(session, 'context'): 449 # SA 0.3 450 tr = session.create_transaction() 451 return tr 452 else: 453 # SA 0.4 454 session.begin() 455 return session 459 """Create a new transaction in an SA session.""" 460 try: 461 return session.begin() 462 except AttributeError: # SA < 0.4 463 return session.create_transaction() 464 465 def sa_transaction_active(transaction): 466 """Check whether SA transaction is still active.""" 467 try: 468 return transaction and transaction.is_active 469 except AttributeError: # SA < 0.4.3 470 return transaction.session.transaction 456 471 457 472 def so_to_dict(sqlobj): 458 " Converts SQLObject to a dictionary based on columns"473 """Convert SQLObject to a dictionary based on columns.""" 459 474 d = {} 460 475 if sqlobj == None: … … 471 486 472 487 def so_columns(sqlclass, columns=None): 473 """Returns a dict with all columns from a SQLObject including those from 474 InheritableSO's bases""" 488 """Return a dict with all columns from a SQLObject. 489 490 This includes the columns from InheritableSO's bases. 491 492 """ 475 493 if columns is None: 476 494 columns = {} … … 482 500 483 501 def so_joins(sqlclass, joins=None): 484 """Returns a list with all joins from a SQLObject including those from 485 InheritableSO's bases""" 502 """Return a list with all joins from a SQLObject. 503 504 The list includes the columns from InheritableSO's bases. 505 506 """ 486 507 if joins is None: 487 508 joins = [] branches/1.0/turbogears/tests/test_paginate.py
r4122 r4132 1 " Tests for paginate"1 """Tests for paginate""" 2 2 3 3 import unittest branches/1.0/turbogears/tests/test_sqlalchemy.py
r4122 r4132 1 " Tests for SQLAlchemy support"1 """Tests for SQLAlchemy support""" 2 2 3 3 import cherrypy, os, threading, turbogears 4 4 5 from sqlalchemy import *5 from sqlalchemy import MetaData, Table, Column, ForeignKey, Integer, String 6 6 from sqlalchemy.ext.activemapper import ActiveMapper, column, one_to_many 7 7 8 from turbogears import config, redirect, expose, database, errorhandling 9 from turbogears.testutil import create_request, capture_log, print_log, \ 10 sqlalchemy_cleanup 8 from turbogears import config, redirect, expose, errorhandling 11 9 from turbogears.database import get_engine, metadata, session, mapper 12 10 from turbogears.controllers import RootController 13 11 from turbogears.testutil import create_request, sqlalchemy_cleanup, \ 12 capture_log, print_log 13 14 15 # Fixture 14 16 15 17 class User(object): … … 68 70 def teardown_module(): 69 71 metadata.drop_all() 70 sqlalchemy_cleanup()71 72 fresh_metadata.drop_all() 72 73 fresh_metadata.bind.dispose() 73 74 if os.path.exists('freshtest.db'): 74 75 os.unlink('freshtest.db') 75 76 sqlalchemy_cleanup() 77 78 79 # Simple database tests 76 80 77 81 def test_query_in_session(): … … 102 106 assert len(ford.addresses) == 1 103 107 108 109 # Exception handling 110 104 111 class MyRoot(RootController): 112 """A small root controller for our exception handling tests""" 113 105 114 def no_error(self, name): 106 p = Person(name=name) 115 """Test controller""" 116 Person(name=name) 107 117 raise redirect("/confirm") 108 118 no_error = expose()(no_error) 109 119 110 120 def e_handler(self, tg_exceptions=None): 121 """Test error handler""" 111 122 cherrypy.response.code = 501 112 return "An exception ocurred: %r (%s)" % ((tg_exceptions,)*2) 113 114 def create_person(self, id, docom=0, doerr=0): 115 p = Person(id=id) 123 return "An exception occurred: %r (%s)" % ((tg_exceptions,)*2) 124 125 def create_person(self, id, docom=0, doerr=0, doflush=0): 126 """Test controller""" 127 Person(id=id) 116 128 if int(docom): 117 129 cherrypy.request.sa_transaction.commit() … … 120 132 if int(doerr) == 2: 121 133 raise turbogears.redirect('/') 122 return "No exceptions ocurred" 134 if int(doflush): 135 try: 136 session.flush() 137 except Exception: 138 if int(doflush) == 1: 139 raise 140 return "No exceptions occurred" 123 141 create_person = expose()(create_person) 124 142 create_person = errorhandling.exception_handler(e_handler)(create_person) 125 143 144 126 145 def test_implicit_trans_no_error(): 146 """If a controller runs sucessfully, the transaction is commited.""" 127 147 capture_log("turbogears.database") 128 148 cherrypy.root = MyRoot() … … 134 154 135 155 def test_raise_sa_exception(): 156 """If a controller causes an SA exception, it is raised properly.""" 136 157 capture_log("turbogears.database") 137 158 cherrypy.root = MyRoot() 138 159 create_request("/create_person?id=20") 139 160 output = cherrypy.response.body[0] 140 print output 141 assert "No exceptions" in output 142 161 assert 'No exceptions occurred' in output 143 162 create_request("/create_person?id=20") 144 163 output = cherrypy.response.body[0] 145 print output 146 147 # Note that the specific DB2API may be either OperationalError or 148 # IntegrityError depending on what version of sqlite and pysqlite 149 # is used. 150 # SA 0.3 uses SQLError; 0.4 DBAPIError 151 assert "SQLError" in output or "DBAPIError" in output 152 153 # Check that if a controller raises an exception, transactions are rolled back 164 # SA 0.3 uses SQLError, 0.4 DBAPIError 165 assert 'SQLError' in output or 'DBAPIError' in output 166 154 167 def test_user_exception(): 168 """If a controller raises an exception, transactions are rolled back.""" 155 169 cherrypy.root = MyRoot() 156 170 create_request("/create_person?id=21&doerr=1") … … 158 172 assert Person.query().get(21) is None 159 173 160 # Check that if a controller redirects, transactions are committed161 174 def test_user_redirect(): 175 """If a controller redirects, transactions are committed.""" 162 176 cherrypy.root = MyRoot() 163 177 create_request("/create_person?id=22&doerr=2") … … 165 179 assert Person.query().get(22) is not None 166 180 167 # Check it's safe to commit a transaction in the controller168 181 def test_cntrl_commit(): 182 """It's safe to commit a transaction in the controller.""" 169 183 cherrypy.root = MyRoot() 170 184 create_request("/create_person?id=23&docom=1") 171 assert 'InvalidRequestError: This transaction is inactive' not in cherrypy.response.body[0] 172 173 # Check an exception within a tg.exception_handler causes a rollback 185 assert 'InvalidRequestError' not in cherrypy.response.body[0] 186 187 def test_cntrl_flush(): 188 """It's safe to flush in the controller.""" 189 cherrypy.root = MyRoot() 190 create_request("/create_person?id=24&doflush=1") 191 assert 'No exceptions occurred' in cherrypy.response.body[0] 192 create_request("/create_person?id=24&doflush=0") 193 assert 'IntegrityError' in cherrypy.response.body[0] 194 create_request("/create_person?id=24&doflush=1") 195 assert 'IntegrityError' in cherrypy.response.body[0] 196 create_request("/create_person?id=24&doflush=2") 197 assert 'No exceptions occurred' in cherrypy.response.body[0] 198 199 200 # Exception handling with rollback 201 174 202 class RbRoot(RootController): 203 """A small root controller for our transaction rollback tests""" 204 175 205 def handerr(self, id): 206 """Test error handler""" 176 207 Person(id=int(id)+1) 177 208 return dict() 209 178 210 def doerr(self, id, dorb=0): 211 """Test controller""" 179 212 Person(id=id) 180 213 if int(dorb): … … 184 217 doerr = expose()(doerr) 185 218 219 186 220 def test_exc_rollback(): 221 """"An exception within a controller method causes a rollback.""" 187 222 cherrypy.root = RbRoot() 188 create_request('/doerr?id=24') 189 print cherrypy.response.body[0] 190 assert Person.query().get(24) is None 191 assert Person.query().get(25) is not None 192 193 # Check that if controller method manually rollbacks, error handler doesn't cause problems 223 create_request('/doerr?id=25') 224 assert Person.query().get(25) is None 225 assert Person.query().get(26) is not None 226 194 227 def test_exc_done_rollback(): 228 """No problems with error handler if controller manually rollbacks.""" 195 229 cherrypy.root = RbRoot() 196 create_request('/doerr?id=26&dorb=1') 197 print cherrypy.response.body[0] 230 create_request('/doerr?id=27&dorb=1') 198 231 assert cherrypy.response.body[0] == '{"tg_flash": null}' 199 232 200 #-- 201 # Check for session freshness, ticket #1419 202 # It checks that changes made to the data in thread B are reflected in thread A. 203 #-- 233 234 # Session freshness tests 204 235 205 236 class FreshRoot(RootController): 237 """A small root controller for our session freshness tests""" 238 206 239 def test1(self): 207 240 assert session.query(Test).get(1).val == 'a' 208 241 return dict() 209 242 test1 = expose()(test1) 243 210 244 def test2(self): 211 245 session.query(Test).get(1).val = 'b' 212 246 return dict() 213 247 test2 = expose()(test2) 248 214 249 def test3(self): 215 250 assert session.query(Test).get(1).val == 'b' … … 217 252 test3 = expose()(test3) 218 253 254 219 255 def test_session_freshness(): 256 """Check for session freshness. 257 258 Changes made to the data in thread B should be reflected in thread A. 259 260 """ 220 261 fresh_metadata.bind.execute(test_table.insert(), dict(id=1, val='a')) 221 222 262 cherrypy.root = FreshRoot() 223 224 263 create_request("/test1") 225 print cherrypy.response.body[0]226 264 assert 'AssertionError' not in cherrypy.response.body[0] 227 228 265 # Call test2 in a different thread 229 266 class ThreadB(threading.Thread): 230 267 def run(self): 231 268 create_request("/test2") 232 print cherrypy.response.body[0]233 269 assert 'AssertionError' not in cherrypy.response.body[0] 234 270 thrdb = ThreadB() 235 271 thrdb.start() 236 272 thrdb.join() 237 238 273 create_request("/test3") 239 print cherrypy.response.body[0]240 274 assert 'AssertionError' not in cherrypy.response.body[0] branches/1.0/turbogears/testutil.py
r3967 r4132 1 import os 1 2 import types 2 import inspect3 3 import logging 4 4 import unittest 5 import Cookie 5 6 import cStringIO as StringIO 6 import Cookie7 7 8 8 import cherrypy 9 from cherrypy import _cphttptools 10 9 11 try: 10 12 import sqlobject … … 12 14 except ImportError: 13 15 sqlobject = None 14 15 16 try: 16 17 import sqlalchemy … … 18 19 sqlalchemy = None 19 20 20 from cherrypy import _cphttptools 21 22 from turbogears import database, controllers, startup, validators, config, \ 23 update_config 21 from turbogears import startup, config, update_config, \ 22 controllers, database, validators 23 from turbogears.identity import current_provider 24 24 from turbogears.util import get_model 25 26 import os27 from os.path import *28 25 29 26 cwd = os.getcwd() … … 34 31 for f in w[2]: 35 32 if f.endswith('.kid'): 36 f = join(w[0], f[:-3] + 'pyc')37 if exists(f):33 f = os.path.join(w[0], f[:-3] + 'pyc') 34 if os.path.exists(f): 38 35 os.remove(f) 39 36 40 37 # Override config of all applications with test.cfg 41 if exists(join(cwd, "test.cfg")):38 if os.path.exists(os.path.join(cwd, "test.cfg")): 42 39 modulename = None 43 40 for w in os.walk(cwd): … … 46 43 modulename = "%s.app" % config_dir.replace(os.sep, ".") 47 44 break 48 update_config(configfile=join(cwd, "test.cfg"), modulename=modulename) 45 update_config(configfile=os.path.join(cwd, "test.cfg"), 46 modulename=modulename) 49 47 else: 50 48 database.set_db_uri("sqlite:///:memory:") 51 49 52 config.update({"global" : {"tg.new_style_logging" : True}}) 53 config.update({"global" : {"autoreload.on" : False}}) 50 config.update({'global': 51 {'autoreload.on': False, 'tg.new_style_logging': True}}) 52 54 53 55 54 def start_cp(): … … 58 57 config.update({"cherrypy_started" : True}) 59 58 59 60 60 test_user = None 61 61 62 62 63 def set_identity_user(user): 63 " Setup a user which will be used to configure request's identity."64 """Setup a user for configuring request's identity.""" 64 65 global test_user 65 66 test_user = user 66 67 68 67 69 def attach_identity(req): 68 from turbogears.identity import current_provider69 70 if config.get("identity.on", False): 70 if test_user: 71 id = current_provider.authenticated_identity(test_user) 72 else: 73 id = current_provider.anonymous_identity() 74 req.identity = id 71 req.identity = (test_user 72 and current_provider.authenticated_identity(test_user) 73 or current_provider.anonymous_identity()) 74 75 75 76 76 def create_request(request, method="GET", protocol="HTTP/1.1", 77 headers={}, rfile=None, clientAddress="127.0.0.1",78 remoteHost="localhost", scheme="http"):77 headers={}, rfile=None, clientAddress="127.0.0.1", 78 remoteHost="localhost", scheme="http"): 79 79 start_cp() 80 80 if not rfile: … … 94 94 req.run(" ".join((method, request, protocol)), headerList, rfile) 95 95 96 createRequest = create_request 96 createRequest = create_request # deprecated 97 97 98 98 99 99 class BrowsingSession(object): 100 100 101 def __init__(self): 101 102 self.visit = None … … 117 118 return output 118 119 120 119 121 class DummySession: 120 122 session_storage = dict 121 123 to_be_loaded = None 122 124 125 123 126 class DummyRequest: 124 "A very simple dummy request." 127 """A very simple dummy request.""" 128 125 129 remote_host = "127.0.0.1" 126 130 … … 131 135 self.base = '' 132 136 self._session = DummySession() 137 133 138 def purge__(self): 134 139 pass 140 135 141 136 142 def call(method, *args, **kw): … … 139 145 return output 140 146 147 141 148 def call_with_request(method, request, *args, **kw): 142 "More fine-grained version of call method, allowing to use request/response." 149 """More fine-grained version of call method. 150 151 This allows using request/response. 152 153 """ 143 154 orig_proc_output = controllers._process_output 144 155 controllers._process_output = _return_directly … … 158 169 159 170 class DBTest(unittest.TestCase): 171 160 172 model = None 161 173 … … 187 199 item.dropTable(ifExists=True) 188 200 201 189 202 def reset_cp(): 190 203 cherrypy.root = None 191 204 205 192 206 def catch_validation_errors(widget, value): 193 """ Catches and unpacks validation errors. For testing purposes. """ 194 errors = {} 207 """Catch and unpack validation errors (for testing purposes).""" 195 208 try: 196 209 value = widget.validate(value) 197 except validators.Invalid, e: 198 if hasattr(e, 'unpack_errors'): 199 errors = e.unpack_errors() 200 else: 201 errors = e 210 except validators.Invalid, errors: 211 try: 212 errors = errors.unpack_errors() 213 except AttributeError: 214 pass 215 else: 216 errors = {} 202 217 return value, errors 218 203 219 204 220 class MemoryListHandler(logging.Handler): … … 223 239 224 240 _memhandler = MemoryListHandler() 241 242 225 243 _currentcat = None 226 244 245 227 246 def capture_log(category): 228 """Category can either be a single category (a string like 'foo.bar') 229 or a list of them. You <em>must</em> call print_log() to reset when 230 you're done.""" 247 """Capture log for one category. 248 249 The category can either be a single category (a string like 'foo.bar') 250 or a list of them. You *must* call print_log() to reset when you're done. 251 252 """ 231 253 global _currentcat 232 254 assert not _currentcat … … 239 261 log.addHandler(_memhandler) 240 262 263 241 264 def _reset_logging(): 242 """Manage s the resetting of the loggers"""265 """Manage the resetting of the loggers.""" 243 266 global _currentcat 244 267 if not _currentcat: … … 249 272 _currentcat = None 250 273 274 251 275 def print_log(): 252 """Prints the log captured by capture_log to stdout, resets that log 253 and resets the temporarily added handlers.""" 276 """Print the log captured by capture_log to stdout. 277 278 Resets that log and resets the temporarily added handlers. 279 280 """ 254 281 _reset_logging() 255 282 _memhandler.print_log() 256 283 284 257 285 def get_log(): 258 """Returns the list of log messages captured by capture_log, 259 resets that log and resets the temporarily added handlers.""" 286 """Return the list of log messages captured by capture_log. 287 288 Resets that log and resets the temporarily added handlers. 289 290 """ 260 291 _reset_logging() 261 292 return _memhandler.get_log() 262 293 294 263 295 def sqlalchemy_cleanup(): 296 database.metadata.clear() 297 try: 298 database.metadata.dispose() 299 except AttributeError: # not threadlocal 300 if database.metadata.bind: 301 database.metadata.bind.dispose() 264 302 database._engine = None 265 sqlalchemy.orm.clear_mappers() 266 database.metadata.clear() 267 try: # if ThreadLocalMetaData is used 268 database.metadata.dispose() 269 except AttributeError: 270 pass 303 if database.mapper == database.session.mapper: 304 # the following does not work for SA < 0.4 305 sqlalchemy.orm.clear_mappers() 306 271 307 272 308 __all__ = ["create_request", "call", "DBTest", "createRequest", branches/1.1/turbogears/database.py
r4129 r4132 1 """Provides convenient access to an SQLObject or SQLAlchemy 2 managed database.""" 1 """Convenient access to an SQLObject or SQLAlchemy managed database.""" 3 2 4 3 import sys … … 176 175 177 176 def _is_interesting_version(self): 178 " Return True only if version of MySQLdb <= 1.0."177 """Return True only if version of MySQLdb <= 1.0.""" 179 178 import MySQLdb 180 179 module_version = MySQLdb.version_info[0:2] … … 227 226 228 227 def begin(self, conn=None): 229 " Starts a transaction."228 """Start a transaction.""" 230 229 if not self.supports_transactions: 231 230 return conn … … 242 241 243 242 def commit(self): 244 " Commits the current transaction."243 """Commit the current transaction.""" 245 244 if not self.supports_transactions: 246 245 return … … 253 252 254 253 def rollback(self): 255 " Rolls back the current transaction."254 """Rollback the current transaction.""" 256 255 if not self.supports_transactions: 257 256 return … … 264 263 265 264 def end(self): 266 " Ends the transaction, returning to a standard connection."265 """End the transaction, returning to a standard connection.""" 267 266 if not self.supports_transactions: 268 267 return … … 349 348 350 349 def commit_all(): 351 " Commits the Transactions in all registered hubs (for this thread)"350 """Commit the transactions in all registered hubs (for this thread).""" 352 351 for hub in hub_registry: 353 352 hub.commit() 354 353 355 354 def rollback_all(): 356 " Rolls back the Transactions in all registered hubs (for this thread)"355 """Rollback the transactions in all registered hubs (for this thread).""" 357 356 for hub in hub_registry: 358 357 hub.rollback() 359 358 360 359 def end_all(): 361 " Ends the Transactions in all registered hubs (for this thread)"360 """End the transactions in all registered hubs (for this thread).""" 362 361 for hub in hub_registry: 363 362 hub.end() … … 390 389 raise 391 390 except: 392 # No need to "rollback" the sqlalchemy unit of work, because nothing393 # has hit the db yet.391 # No need to "rollback" the sqlalchemy unit of work, 392 # because nothing has hit the db yet. 394 393 rollback_all() 395 394 raise … … 425 424 def sa_rwt(func, *args, **kw): 426 425 log.debug("Starting SA transaction") 427 session.begin() 426 request = cherrypy.request 427 request.sa_transaction = session.begin() 428 428 try: 429 cherrypy.request.sa_transaction = session # for compatibility430 429 try: 431 430 retval = func(*args, **kw) 432 431 except (cherrypy.HTTPRedirect, cherrypy.InternalRedirect): 433 log.debug('Redirect in transaction - will commit now')434 432 # If a redirect happens, commit and proceed with redirect. 435 session.commit() 433 if sa_transaction_active(request.sa_transaction): 434 log.debug('Redirect in active transaction - will commit now') 435 session.commit() 436 else: 437 log.debug('Redirect in inactive transaction') 436 438 raise 437 439 except: 438 log.debug('Error in transaction - will rollback now')439 440 # If any other exception happens, rollback and re-raise error 440 session.rollback() 441 if sa_transaction_active(request.sa_transaction): 442 log.debug('Error in active transaction - will rollback now') 443 session.rollback() 444 else: 445 log.debug('Error in inactive transaction') 441 446 raise 442 447 # If the call was successful, commit and proceed 443 log.debug('Successful transaction - will commit now') 444 session.commit() 448 if sa_transaction_active(request.sa_transaction): 449 log.debug('Transaction is still active - will commit now') 450 session.commit() 451 else: 452 log.debug('Transaction is already inactive') 445 453 finally: 446 454 log.debug('Ending SA transaction') … … 452 460 def sa_restart_transaction(args): 453 461 log.debug("Restarting SA transaction") 454 session.rollback() 455 session.clear() 456 session.begin() 462 request = cherrypy.request 463 if sa_transaction_active(request.sa_transaction): 464 log.debug('Transaction is still active - will rollback now') 465 session.rollback() 466 else: 467 log.debug('Transaction is already inactive') 468 session.close() 469 request.sa_transaction = session.begin() 470 471 def sa_transaction_active(transaction): 472 """Check whether SA transaction is still active.""" 473 try: 474 return transaction and transaction.is_active 475 except AttributeError: # SA < 0.4.3 476 return transaction.session.transaction 457 477 458 478 def so_to_dict(sqlobj): 459 " Converts SQLObject to a dictionary based on columns"479 """Convert SQLObject to a dictionary based on columns.""" 460 480 d = {} 461 481 if sqlobj == None: … … 472 492 473 493 def so_columns(sqlclass, columns=None): 474 """Returns a dict with all columns from a SQLObject including those from 475 InheritableSO's bases""" 494 """Return a dict with all columns from a SQLObject. 495 496 This includes the columns from InheritableSO's bases. 497 498 """ 476 499 if columns is None: 477 500 columns = {} … … 483 506 484 507 def so_joins(sqlclass, joins=None): 485 """Returns a list with all joins from a SQLObject including those from 486 InheritableSO's bases""" 508 """Return a list with all joins from a SQLObject. 509 510 The list includes the columns from InheritableSO's bases. 511 512 """ 487 513 if joins is None: 488 514 joins = [] branches/1.1/turbogears/tests/test_paginate.py
r4122 r4132 1 " Tests for paginate"1 """Tests for paginate""" 2 2 3 3 import unittest branches/1.1/turbogears/tests/test_sqlalchemy.py
r4122 r4132 1 " Tests for SQLAlchemy support"1 """Tests for SQLAlchemy support""" 2 2 3 3 import cherrypy, os, threading, turbogears … … 6 6 from sqlalchemy.orm import relation 7 7 8 from turbogears import config, redirect, expose, database, errorhandling 9 from turbogears.testutil import capture_log, print_log, \ 10 create_request, sqlalchemy_cleanup 8 from turbogears import config, redirect, expose, errorhandling 11 9 from turbogears.database import metadata, session, mapper, \ 12 10 bind_metadata, get_metadata 13 11 from turbogears.controllers import RootController 14 12 from turbogears.testutil import create_request, sqlalchemy_cleanup, \ 13 capture_log, print_log 14 15 16 # Fixture 15 17 16 18 class User(object): … … 91 93 92 94 95 # Database tests 96 93 97 def test_query_in_session(): 94 98 i = users_table.insert() … … 114 118 115 119 116 # ------------ Exception Handling --------------- Start.120 # Exception handling 117 121 118 122 class MyRoot(RootController): 123 """A small root controller for our exception handling tests""" 124 125 @expose() 119 126 def no_error(self, name): 120 p = Person() 121 p.name = name 122 session.save(p) 127 """Test controller""" 128 Person(name=name) 123 129 raise redirect("/someconfirmhandler") 124 no_error = expose()(no_error)125 130 126 131 def e_handler(self, id, tg_exceptions=None): 127 """ 128 this handler is called KARL25 and all returns from it 132 """Test error handler 133 134 This handler is called KARL25 and all returns from it 129 135 will be marked with this name :) 136 130 137 """ 131 138 cherrypy.response.code = 501 132 139 msg = "KARL25 responding\n" 133 140 msg += "user with id: '%s' should not be saved.\n" % id 134 msg += "An exception oc urred: %r (%s)" % ((tg_exceptions,)*2)141 msg += "An exception occurred: %r (%s)" % ((tg_exceptions,)*2) 135 142 return dict(msg=msg) 136 143 137 def create_person(self, id, docom=0, doerr=0, name="John Doe"):138 p = Person()139 p.id = id140 p.name = name141 session.save(p)142 144 @expose() 145 @errorhandling.exception_handler(e_handler) 146 def create_person(self, id, 147