| 65 | | class AutoConnectHub(ConnectionHub): |
|---|
| 66 | | """Connects to the database once per thread. The AutoConnectHub also |
|---|
| 67 | | provides convenient methods for managing transactions.""" |
|---|
| 68 | | uri = None |
|---|
| 69 | | params = {} |
|---|
| 70 | | |
|---|
| 71 | | def __init__(self, uri=None, supports_transactions=True): |
|---|
| 72 | | if not uri: |
|---|
| 73 | | uri = config.get("sqlobject.dburi") |
|---|
| 74 | | self.uri = uri |
|---|
| 75 | | self.supports_transactions = supports_transactions |
|---|
| 76 | | hub_registry.add(self) |
|---|
| 77 | | ConnectionHub.__init__(self) |
|---|
| 78 | | |
|---|
| 79 | | def getConnection(self): |
|---|
| 80 | | try: |
|---|
| 81 | | conn = self.threadingLocal.connection |
|---|
| 82 | | return self.begin(conn) |
|---|
| 83 | | except AttributeError: |
|---|
| 84 | | if self.uri: |
|---|
| 85 | | conn = sqlobject.connectionForURI(self.uri) |
|---|
| 86 | | # the following line effectively turns off the DBAPI connection |
|---|
| 87 | | # cache. We're already holding on to a connection per thread, |
|---|
| 88 | | # and the cache causes problems with sqlite. |
|---|
| 89 | | if self.uri.startswith("sqlite"): |
|---|
| 90 | | TheURIOpener.cachedURIs = {} |
|---|
| 91 | | self.threadingLocal.connection = conn |
|---|
| 92 | | return self.begin(conn) |
|---|
| 93 | | raise AttributeError( |
|---|
| 94 | | "No connection has been defined for this thread " |
|---|
| 95 | | "or process") |
|---|
| 96 | | |
|---|
| 97 | | def reset(self): |
|---|
| 98 | | """Used for testing purposes. This drops all of the connections |
|---|
| 99 | | that are being held.""" |
|---|
| 100 | | self.threadingLocal = threading_local() |
|---|
| 101 | | |
|---|
| 102 | | def begin(self, conn=None): |
|---|
| 103 | | "Starts a transaction." |
|---|
| 104 | | if not self.supports_transactions: |
|---|
| 105 | | return conn |
|---|
| 106 | | if not conn: |
|---|
| 107 | | conn = self.getConnection() |
|---|
| 108 | | if isinstance(conn, Transaction): |
|---|
| 109 | | if conn._obsolete: |
|---|
| 110 | | conn.begin() |
|---|
| 111 | | return conn |
|---|
| 112 | | self.threadingLocal.old_conn = conn |
|---|
| 113 | | trans = conn.transaction() |
|---|
| 114 | | self.threadingLocal.connection = trans |
|---|
| 115 | | return trans |
|---|
| 116 | | |
|---|
| 117 | | def commit(self): |
|---|
| 118 | | "Commits the current transaction." |
|---|
| 119 | | if not self.supports_transactions: |
|---|
| 120 | | return |
|---|
| 121 | | try: |
|---|
| 122 | | conn = self.threadingLocal.connection |
|---|
| 123 | | except AttributeError: |
|---|
| 124 | | return |
|---|
| 125 | | if isinstance(conn, Transaction): |
|---|
| 126 | | self.threadingLocal.connection.commit() |
|---|
| 127 | | |
|---|
| 128 | | def rollback(self): |
|---|
| 129 | | "Rolls back the current transaction." |
|---|
| 130 | | if not self.supports_transactions: |
|---|
| 131 | | return |
|---|
| 132 | | try: |
|---|
| 133 | | conn = self.threadingLocal.connection |
|---|
| 134 | | except AttributeError: |
|---|
| 135 | | return |
|---|
| 136 | | if isinstance(conn, Transaction) and not conn._obsolete: |
|---|
| 137 | | self.threadingLocal.connection.rollback() |
|---|
| 138 | | |
|---|
| 139 | | def end(self): |
|---|
| 140 | | "Ends the transaction, returning to a standard connection." |
|---|
| 141 | | if not self.supports_transactions: |
|---|
| 142 | | return |
|---|
| 143 | | try: |
|---|
| 144 | | conn = self.threadingLocal.connection |
|---|
| 145 | | except AttributeError: |
|---|
| 146 | | return |
|---|
| 147 | | if not isinstance(conn, Transaction): |
|---|
| 148 | | return |
|---|
| 149 | | if not conn._obsolete: |
|---|
| 150 | | conn.rollback() |
|---|
| 151 | | self.threadingLocal.connection = self.threadingLocal.old_conn |
|---|
| 152 | | del self.threadingLocal.old_conn |
|---|
| 153 | | self.threadingLocal.connection.cache.clear() |
|---|
| 154 | | |
|---|
| 159 | | class PackageHub(object): |
|---|
| 160 | | """Transparently proxies to an AutoConnectHub for the URI |
|---|
| 161 | | that is appropriate for this package. A package URI is |
|---|
| 162 | | configured via "packagename.dburi" in the global CherryPy |
|---|
| 163 | | settings. If there is no package DB URI configured, the |
|---|
| 164 | | default (provided by "sqlobject.dburi") is used. |
|---|
| 165 | | |
|---|
| 166 | | The hub is not instantiated until an attempt is made to |
|---|
| 167 | | use the database. |
|---|
| 168 | | """ |
|---|
| 169 | | def __init__(self, packagename): |
|---|
| 170 | | self.packagename = packagename |
|---|
| 171 | | self.hub = None |
|---|
| 172 | | |
|---|
| 173 | | def __get__(self, obj, type): |
|---|
| 174 | | if not self.hub: |
|---|
| 175 | | self.set_hub() |
|---|
| 176 | | return self.hub.__get__(obj, type) |
|---|
| 177 | | |
|---|
| 178 | | def __set__(self, obj, type): |
|---|
| 179 | | if not self.hub: |
|---|
| 180 | | self.set_hub() |
|---|
| 181 | | return self.hub.__set__(obj, type) |
|---|
| 182 | | |
|---|
| 183 | | def __getattr__(self, name): |
|---|
| 184 | | if not self.hub: |
|---|
| 185 | | self.set_hub() |
|---|
| 186 | | return getattr(self.hub, name) |
|---|
| 187 | | |
|---|
| 188 | | def set_hub(self): |
|---|
| 189 | | dburi = config.get("%s.dburi" % self.packagename, None) |
|---|
| 190 | | if not dburi: |
|---|
| 191 | | dburi = config.get("sqlobject.dburi", None) |
|---|
| 192 | | if not dburi: |
|---|
| 193 | | raise KeyError, "No database configuration found!" |
|---|
| 194 | | if dburi.startswith("notrans_"): |
|---|
| 195 | | dburi = dburi[8:] |
|---|
| 196 | | trans = False |
|---|
| 197 | | else: |
|---|
| 198 | | trans = True |
|---|
| 199 | | hub = _hubs.get(dburi, None) |
|---|
| 200 | | if not hub: |
|---|
| 201 | | hub = AutoConnectHub(dburi, supports_transactions=trans) |
|---|
| 202 | | _hubs[dburi] = hub |
|---|
| 203 | | self.hub = hub |
|---|
| | 72 | |
|---|
| | 73 | if sqlobject: |
|---|
| | 74 | class AutoConnectHub(ConnectionHub): |
|---|
| | 75 | """Connects to the database once per thread. The AutoConnectHub also |
|---|
| | 76 | provides convenient methods for managing transactions.""" |
|---|
| | 77 | uri = None |
|---|
| | 78 | params = {} |
|---|
| | 79 | |
|---|
| | 80 | def __init__(self, uri=None, supports_transactions=True): |
|---|
| | 81 | if not uri: |
|---|
| | 82 | uri = config.get("sqlobject.dburi") |
|---|
| | 83 | self.uri = uri |
|---|
| | 84 | self.supports_transactions = supports_transactions |
|---|
| | 85 | hub_registry.add(self) |
|---|
| | 86 | ConnectionHub.__init__(self) |
|---|
| | 87 | |
|---|
| | 88 | def getConnection(self): |
|---|
| | 89 | try: |
|---|
| | 90 | conn = self.threadingLocal.connection |
|---|
| | 91 | return self.begin(conn) |
|---|
| | 92 | except AttributeError: |
|---|
| | 93 | if self.uri: |
|---|
| | 94 | conn = sqlobject.connectionForURI(self.uri) |
|---|
| | 95 | # the following line effectively turns off the DBAPI connection |
|---|
| | 96 | # cache. We're already holding on to a connection per thread, |
|---|
| | 97 | # and the cache causes problems with sqlite. |
|---|
| | 98 | if self.uri.startswith("sqlite"): |
|---|
| | 99 | TheURIOpener.cachedURIs = {} |
|---|
| | 100 | self.threadingLocal.connection = conn |
|---|
| | 101 | return self.begin(conn) |
|---|
| | 102 | raise AttributeError( |
|---|
| | 103 | "No connection has been defined for this thread " |
|---|
| | 104 | "or process") |
|---|
| | 105 | |
|---|
| | 106 | def reset(self): |
|---|
| | 107 | """Used for testing purposes. This drops all of the connections |
|---|
| | 108 | that are being held.""" |
|---|
| | 109 | self.threadingLocal = threading_local() |
|---|
| | 110 | |
|---|
| | 111 | def begin(self, conn=None): |
|---|
| | 112 | "Starts a transaction." |
|---|
| | 113 | if not self.supports_transactions: |
|---|
| | 114 | return conn |
|---|
| | 115 | if not conn: |
|---|
| | 116 | conn = self.getConnection() |
|---|
| | 117 | if isinstance(conn, Transaction): |
|---|
| | 118 | if conn._obsolete: |
|---|
| | 119 | conn.begin() |
|---|
| | 120 | return conn |
|---|
| | 121 | self.threadingLocal.old_conn = conn |
|---|
| | 122 | trans = conn.transaction() |
|---|
| | 123 | self.threadingLocal.connection = trans |
|---|
| | 124 | return trans |
|---|
| | 125 | |
|---|
| | 126 | def commit(self): |
|---|
| | 127 | "Commits the current transaction." |
|---|
| | 128 | if not self.supports_transactions: |
|---|
| | 129 | return |
|---|
| | 130 | try: |
|---|
| | 131 | conn = self.threadingLocal.connection |
|---|
| | 132 | except AttributeError: |
|---|
| | 133 | return |
|---|
| | 134 | if isinstance(conn, Transaction): |
|---|
| | 135 | self.threadingLocal.connection.commit() |
|---|
| | 136 | |
|---|
| | 137 | def rollback(self): |
|---|
| | 138 | "Rolls back the current transaction." |
|---|
| | 139 | if not self.supports_transactions: |
|---|
| | 140 | return |
|---|
| | 141 | try: |
|---|
| | 142 | conn = self.threadingLocal.connection |
|---|
| | 143 | except AttributeError: |
|---|
| | 144 | return |
|---|
| | 145 | if isinstance(conn, Transaction) and not conn._obsolete: |
|---|
| | 146 | self.threadingLocal.connection.rollback() |
|---|
| | 147 | |
|---|
| | 148 | def end(self): |
|---|
| | 149 | "Ends the transaction, returning to a standard connection." |
|---|
| | 150 | if not self.supports_transactions: |
|---|
| | 151 | return |
|---|
| | 152 | try: |
|---|
| | 153 | conn = self.threadingLocal.connection |
|---|
| | 154 | except AttributeError: |
|---|
| | 155 | return |
|---|
| | 156 | if not isinstance(conn, Transaction): |
|---|
| | 157 | return |
|---|
| | 158 | if not conn._obsolete: |
|---|
| | 159 | conn.rollback() |
|---|
| | 160 | self.threadingLocal.connection = self.threadingLocal.old_conn |
|---|
| | 161 | del self.threadingLocal.old_conn |
|---|
| | 162 | self.threadingLocal.connection.cache.clear() |
|---|
| | 163 | |
|---|
| | 164 | class PackageHub(object): |
|---|
| | 165 | """Transparently proxies to an AutoConnectHub for the URI |
|---|
| | 166 | that is appropriate for this package. A package URI is |
|---|
| | 167 | configured via "packagename.dburi" in the global CherryPy |
|---|
| | 168 | settings. If there is no package DB URI configured, the |
|---|
| | 169 | default (provided by "sqlobject.dburi") is used. |
|---|
| | 170 | |
|---|
| | 171 | The hub is not instantiated until an attempt is made to |
|---|
| | 172 | use the database. |
|---|
| | 173 | """ |
|---|
| | 174 | def __init__(self, packagename): |
|---|
| | 175 | self.packagename = packagename |
|---|
| | 176 | self.hub = None |
|---|
| | 177 | |
|---|
| | 178 | def __get__(self, obj, type): |
|---|
| | 179 | if not self.hub: |
|---|
| | 180 | self.set_hub() |
|---|
| | 181 | return self.hub.__get__(obj, type) |
|---|
| | 182 | |
|---|
| | 183 | def __set__(self, obj, type): |
|---|
| | 184 | if not self.hub: |
|---|
| | 185 | self.set_hub() |
|---|
| | 186 | return self.hub.__set__(obj, type) |
|---|
| | 187 | |
|---|
| | 188 | def __getattr__(self, name): |
|---|
| | 189 | if not self.hub: |
|---|
| | 190 | self.set_hub() |
|---|
| | 191 | return getattr(self.hub, name) |
|---|
| | 192 | |
|---|
| | 193 | def set_hub(self): |
|---|
| | 194 | dburi = config.get("%s.dburi" % self.packagename, None) |
|---|
| | 195 | if not dburi: |
|---|
| | 196 | dburi = config.get("sqlobject.dburi", None) |
|---|
| | 197 | if not dburi: |
|---|
| | 198 | raise KeyError, "No database configuration found!" |
|---|
| | 199 | if dburi.startswith("notrans_"): |
|---|
| | 200 | dburi = dburi[8:] |
|---|
| | 201 | trans = False |
|---|
| | 202 | else: |
|---|
| | 203 | trans = True |
|---|
| | 204 | hub = _hubs.get(dburi, None) |
|---|
| | 205 | if not hub: |
|---|
| | 206 | hub = AutoConnectHub(dburi, supports_transactions=trans) |
|---|
| | 207 | _hubs[dburi] = hub |
|---|
| | 208 | self.hub = hub |
|---|
| | 209 | else: |
|---|
| | 210 | class AutoConnectHub(object): |
|---|
| | 211 | pass |
|---|
| | 212 | |
|---|
| | 213 | class PackageHub(object): |
|---|
| | 214 | pass |
|---|