76 def get_commit_state(self): |
76 def get_commit_state(self): |
77 return getattr(self._threaddata, 'commit_state', None) |
77 return getattr(self._threaddata, 'commit_state', None) |
78 def set_commit_state(self, value): |
78 def set_commit_state(self, value): |
79 self._threaddata.commit_state = value |
79 self._threaddata.commit_state = value |
80 commit_state = property(get_commit_state, set_commit_state) |
80 commit_state = property(get_commit_state, set_commit_state) |
81 |
81 |
82 # set according to transaction mode for each query |
82 # set according to transaction mode for each query |
83 @property |
83 @property |
84 def pool(self): |
84 def pool(self): |
85 return getattr(self._threaddata, 'pool', None) |
85 return getattr(self._threaddata, 'pool', None) |
86 |
86 |
87 # pending transaction operations |
87 # pending transaction operations |
88 @property |
88 @property |
89 def pending_operations(self): |
89 def pending_operations(self): |
90 try: |
90 try: |
91 return self._threaddata.pending_operations |
91 return self._threaddata.pending_operations |
92 except AttributeError: |
92 except AttributeError: |
93 self._threaddata.pending_operations = [] |
93 self._threaddata.pending_operations = [] |
94 return self._threaddata.pending_operations |
94 return self._threaddata.pending_operations |
95 |
95 |
96 # rql rewriter |
96 # rql rewriter |
97 @property |
97 @property |
98 def rql_rewriter(self): |
98 def rql_rewriter(self): |
99 try: |
99 try: |
100 return self._threaddata._rewriter |
100 return self._threaddata._rewriter |
101 except AttributeError: |
101 except AttributeError: |
102 self._threaddata._rewriter = RQLRewriter(self.repo.querier, self) |
102 self._threaddata._rewriter = RQLRewriter(self.repo.querier, self) |
103 return self._threaddata._rewriter |
103 return self._threaddata._rewriter |
104 |
104 |
105 # transaction queries data |
105 # transaction queries data |
106 @property |
106 @property |
107 def _query_data(self): |
107 def _query_data(self): |
108 try: |
108 try: |
109 return self._threaddata._query_data |
109 return self._threaddata._query_data |
110 except AttributeError: |
110 except AttributeError: |
111 self._threaddata._query_data = {} |
111 self._threaddata._query_data = {} |
112 return self._threaddata._query_data |
112 return self._threaddata._query_data |
113 |
113 |
114 def set_language(self, language): |
114 def set_language(self, language): |
115 """i18n configuration for translation""" |
115 """i18n configuration for translation""" |
116 vreg = self.vreg |
116 vreg = self.vreg |
117 language = language or self.user.property_value('ui.language') |
117 language = language or self.user.property_value('ui.language') |
118 try: |
118 try: |
122 try: |
122 try: |
123 self._ = self.__ = vreg.config.translations[language] |
123 self._ = self.__ = vreg.config.translations[language] |
124 except KeyError: |
124 except KeyError: |
125 self._ = self.__ = unicode |
125 self._ = self.__ = unicode |
126 self.lang = language |
126 self.lang = language |
127 |
127 |
128 def change_property(self, prop, value): |
128 def change_property(self, prop, value): |
129 assert prop == 'lang' # this is the only one changeable property for now |
129 assert prop == 'lang' # this is the only one changeable property for now |
130 self.set_language(value) |
130 self.set_language(value) |
131 |
131 |
132 def __str__(self): |
132 def __str__(self): |
133 return '<%ssession %s (%s 0x%x)>' % (self.cnxtype, self.user.login, |
133 return '<%ssession %s (%s 0x%x)>' % (self.cnxtype, self.user.login, |
134 self.id, id(self)) |
134 self.id, id(self)) |
135 |
135 |
136 def etype_class(self, etype): |
136 def etype_class(self, etype): |
137 """return an entity class for the given entity type""" |
137 """return an entity class for the given entity type""" |
138 return self.vreg.etype_class(etype) |
138 return self.vreg.etype_class(etype) |
139 |
139 |
140 def entity(self, eid): |
140 def entity(self, eid): |
141 """return a result set for the given eid""" |
141 """return a result set for the given eid""" |
142 return self.eid_rset(eid).get_entity(0, 0) |
142 return self.eid_rset(eid).get_entity(0, 0) |
143 |
143 |
144 def _touch(self): |
144 def _touch(self): |
145 """update latest session usage timestamp and reset mode to read |
145 """update latest session usage timestamp and reset mode to read |
146 """ |
146 """ |
147 self.timestamp = time() |
147 self.timestamp = time() |
148 self.local_perm_cache.clear() |
148 self.local_perm_cache.clear() |
149 self._threaddata.mode = 'read' |
149 self._threaddata.mode = 'read' |
150 |
150 |
151 def set_pool(self): |
151 def set_pool(self): |
152 """the session need a pool to execute some queries""" |
152 """the session need a pool to execute some queries""" |
153 if self.pool is None: |
153 if self.pool is None: |
154 self._threaddata.pool = self.repo._get_pool() |
154 self._threaddata.pool = self.repo._get_pool() |
155 try: |
155 try: |
156 self._threaddata.pool.pool_set(self) |
156 self._threaddata.pool.pool_set(self) |
157 except: |
157 except: |
158 self.repo._free_pool(self.pool) |
158 self.repo._free_pool(self.pool) |
159 self._threaddata.pool = None |
159 self._threaddata.pool = None |
160 raise |
160 raise |
161 return self._threaddata.pool |
161 return self._threaddata.pool |
162 |
162 |
163 def reset_pool(self): |
163 def reset_pool(self): |
164 """the session has no longer using its pool, at least for some time |
164 """the session has no longer using its pool, at least for some time |
165 """ |
165 """ |
166 # pool may be none if no operation has been done since last commit |
166 # pool may be none if no operation has been done since last commit |
167 # or rollback |
167 # or rollback |
168 if self.pool is not None and self.mode == 'read': |
168 if self.pool is not None and self.mode == 'read': |
169 # even in read mode, we must release the current transaction |
169 # even in read mode, we must release the current transaction |
170 self.repo._free_pool(self.pool) |
170 self.repo._free_pool(self.pool) |
171 self.pool.pool_reset(self) |
171 self.pool.pool_reset(self) |
172 self._threaddata.pool = None |
172 self._threaddata.pool = None |
173 |
173 |
174 def system_sql(self, sql, args=None): |
174 def system_sql(self, sql, args=None): |
175 """return a sql cursor on the system database""" |
175 """return a sql cursor on the system database""" |
176 if not sql.split(None, 1)[0].upper() == 'SELECT': |
176 if not sql.split(None, 1)[0].upper() == 'SELECT': |
177 self.mode = 'write' |
177 self.mode = 'write' |
178 cursor = self.pool['system'] |
178 cursor = self.pool['system'] |
179 self.pool.source('system').doexec(cursor, sql, args) |
179 self.pool.source('system').doexec(cursor, sql, args) |
180 return cursor |
180 return cursor |
181 |
181 |
182 def actual_session(self): |
182 def actual_session(self): |
183 """return the original parent session if any, else self""" |
183 """return the original parent session if any, else self""" |
184 return self |
184 return self |
185 |
185 |
186 # shared data handling ################################################### |
186 # shared data handling ################################################### |
187 |
187 |
188 def get_shared_data(self, key, default=None, pop=False): |
188 def get_shared_data(self, key, default=None, pop=False): |
189 """return value associated to `key` in session data""" |
189 """return value associated to `key` in session data""" |
190 if pop: |
190 if pop: |
191 return self.data.pop(key, default) |
191 return self.data.pop(key, default) |
192 else: |
192 else: |
193 return self.data.get(key, default) |
193 return self.data.get(key, default) |
194 |
194 |
195 def set_shared_data(self, key, value, querydata=False): |
195 def set_shared_data(self, key, value, querydata=False): |
196 """set value associated to `key` in session data""" |
196 """set value associated to `key` in session data""" |
197 if querydata: |
197 if querydata: |
198 self.set_query_data(key, value) |
198 self.set_query_data(key, value) |
199 else: |
199 else: |
200 self.data[key] = value |
200 self.data[key] = value |
201 |
201 |
202 # request interface ####################################################### |
202 # request interface ####################################################### |
203 |
203 |
204 def set_entity_cache(self, entity): |
204 def set_entity_cache(self, entity): |
205 # no entity cache in the server, too high risk of inconsistency |
205 # no entity cache in the server, too high risk of inconsistency |
206 # between pre/post hooks |
206 # between pre/post hooks |
207 pass |
207 pass |
208 |
208 |
209 def entity_cache(self, eid): |
209 def entity_cache(self, eid): |
210 raise KeyError(eid) |
210 raise KeyError(eid) |
211 |
211 |
212 def base_url(self): |
212 def base_url(self): |
213 return self.repo.config['base-url'] or u'' |
213 return self.repo.config['base-url'] or u'' |
214 |
214 |
215 def from_controller(self): |
215 def from_controller(self): |
216 """return the id (string) of the controller issuing the request (no |
216 """return the id (string) of the controller issuing the request (no |
217 sense here, always return 'view') |
217 sense here, always return 'view') |
218 """ |
218 """ |
219 return 'view' |
219 return 'view' |
220 |
220 |
221 def source_defs(self): |
221 def source_defs(self): |
222 return self.repo.source_defs() |
222 return self.repo.source_defs() |
223 |
223 |
224 def describe(self, eid): |
224 def describe(self, eid): |
225 """return a tuple (type, sourceuri, extid) for the entity with id <eid>""" |
225 """return a tuple (type, sourceuri, extid) for the entity with id <eid>""" |
226 return self.repo.type_and_source_from_eid(eid, self) |
226 return self.repo.type_and_source_from_eid(eid, self) |
227 |
227 |
228 # db-api like interface ################################################### |
228 # db-api like interface ################################################### |
229 |
229 |
230 def source_from_eid(self, eid): |
230 def source_from_eid(self, eid): |
231 """return the source where the entity with id <eid> is located""" |
231 """return the source where the entity with id <eid> is located""" |
232 return self.repo.source_from_eid(eid, self) |
232 return self.repo.source_from_eid(eid, self) |
264 |
264 |
265 @property |
265 @property |
266 def cursor(self): |
266 def cursor(self): |
267 """return a rql cursor""" |
267 """return a rql cursor""" |
268 return self |
268 return self |
269 |
269 |
270 def execute(self, rql, kwargs=None, eid_key=None, build_descr=True, |
270 def execute(self, rql, kwargs=None, eid_key=None, build_descr=True, |
271 propagate=False): |
271 propagate=False): |
272 """db-api like method directly linked to the querier execute method |
272 """db-api like method directly linked to the querier execute method |
273 |
273 |
274 Becare that unlike actual cursor.execute, `build_descr` default to |
274 Becare that unlike actual cursor.execute, `build_descr` default to |
275 false |
275 false |
276 """ |
276 """ |
277 rset = self._execute(self, rql, kwargs, eid_key, build_descr) |
277 rset = self._execute(self, rql, kwargs, eid_key, build_descr) |
278 return self.decorate_rset(rset, propagate) |
278 return self.decorate_rset(rset, propagate) |
279 |
279 |
280 def commit(self, reset_pool=True): |
280 def commit(self, reset_pool=True): |
281 """commit the current session's transaction""" |
281 """commit the current session's transaction""" |
282 if self.pool is None: |
282 if self.pool is None: |
283 assert not self.pending_operations |
283 assert not self.pending_operations |
284 self._query_data.clear() |
284 self._query_data.clear() |
338 self._touch() |
338 self._touch() |
339 self.pending_operations[:] = [] |
339 self.pending_operations[:] = [] |
340 self._query_data.clear() |
340 self._query_data.clear() |
341 if reset_pool: |
341 if reset_pool: |
342 self.reset_pool() |
342 self.reset_pool() |
343 |
343 |
344 def close(self): |
344 def close(self): |
345 """do not close pool on session close, since they are shared now""" |
345 """do not close pool on session close, since they are shared now""" |
346 self.rollback() |
346 self.rollback() |
347 |
347 |
348 # transaction data/operations management ################################## |
348 # transaction data/operations management ################################## |
349 |
349 |
350 def add_query_data(self, key, value): |
350 def add_query_data(self, key, value): |
351 self._query_data.setdefault(key, []).append(value) |
351 self._query_data.setdefault(key, []).append(value) |
352 |
352 |
353 def set_query_data(self, key, value): |
353 def set_query_data(self, key, value): |
354 self._query_data[key] = value |
354 self._query_data[key] = value |
355 |
355 |
356 def query_data(self, key, default=None, setdefault=False, pop=False): |
356 def query_data(self, key, default=None, setdefault=False, pop=False): |
357 if setdefault: |
357 if setdefault: |
358 assert not pop |
358 assert not pop |
359 return self._query_data.setdefault(key, default) |
359 return self._query_data.setdefault(key, default) |
360 if pop: |
360 if pop: |
361 return self._query_data.pop(key, default) |
361 return self._query_data.pop(key, default) |
362 else: |
362 else: |
363 return self._query_data.get(key, default) |
363 return self._query_data.get(key, default) |
364 |
364 |
365 def add_operation(self, operation, index=None): |
365 def add_operation(self, operation, index=None): |
366 """add an observer""" |
366 """add an observer""" |
367 assert self.commit_state != 'commit' |
367 assert self.commit_state != 'commit' |
368 if index is not None: |
368 if index is not None: |
369 self.pending_operations.insert(index, operation) |
369 self.pending_operations.insert(index, operation) |
370 else: |
370 else: |
371 self.pending_operations.append(operation) |
371 self.pending_operations.append(operation) |
372 |
372 |
373 # querier helpers ######################################################### |
373 # querier helpers ######################################################### |
374 |
374 |
375 def build_description(self, rqlst, args, result): |
375 def build_description(self, rqlst, args, result): |
376 """build a description for a given result""" |
376 """build a description for a given result""" |
377 if len(rqlst.children) == 1 and len(rqlst.children[0].solutions) == 1: |
377 if len(rqlst.children) == 1 and len(rqlst.children[0].solutions) == 1: |
378 # easy, all lines are identical |
378 # easy, all lines are identical |
379 selected = rqlst.children[0].selection |
379 selected = rqlst.children[0].selection |
468 def get_commit_state(self): |
468 def get_commit_state(self): |
469 return self.parent_session.commit_state |
469 return self.parent_session.commit_state |
470 def set_commit_state(self, value): |
470 def set_commit_state(self, value): |
471 self.parent_session.set_commit_state(value) |
471 self.parent_session.set_commit_state(value) |
472 commit_state = property(get_commit_state, set_commit_state) |
472 commit_state = property(get_commit_state, set_commit_state) |
473 |
473 |
474 @property |
474 @property |
475 def pool(self): |
475 def pool(self): |
476 return self.parent_session.pool |
476 return self.parent_session.pool |
477 @property |
477 @property |
478 def pending_operations(self): |
478 def pending_operations(self): |
479 return self.parent_session.pending_operations |
479 return self.parent_session.pending_operations |
480 @property |
480 @property |
481 def _query_data(self): |
481 def _query_data(self): |
482 return self.parent_session._query_data |
482 return self.parent_session._query_data |
483 |
483 |
484 def set_pool(self): |
484 def set_pool(self): |
485 """the session need a pool to execute some queries""" |
485 """the session need a pool to execute some queries""" |
486 self.parent_session.set_pool() |
486 self.parent_session.set_pool() |
487 |
487 |
488 def reset_pool(self): |
488 def reset_pool(self): |
489 """the session has no longer using its pool, at least for some time |
489 """the session has no longer using its pool, at least for some time |
490 """ |
490 """ |
491 self.parent_session.reset_pool() |
491 self.parent_session.reset_pool() |
492 |
492 |
493 def actual_session(self): |
493 def actual_session(self): |
494 """return the original parent session if any, else self""" |
494 """return the original parent session if any, else self""" |
495 return self.parent_session |
495 return self.parent_session |
496 |
496 |
497 def commit(self, reset_pool=True): |
497 def commit(self, reset_pool=True): |
498 """commit the current session's transaction""" |
498 """commit the current session's transaction""" |
499 self.parent_session.commit(reset_pool) |
499 self.parent_session.commit(reset_pool) |
500 |
500 |
501 def rollback(self, reset_pool=True): |
501 def rollback(self, reset_pool=True): |
502 """rollback the current session's transaction""" |
502 """rollback the current session's transaction""" |
503 self.parent_session.rollback(reset_pool) |
503 self.parent_session.rollback(reset_pool) |
504 |
504 |
505 def close(self): |
505 def close(self): |
506 """do not close pool on session close, since they are shared now""" |
506 """do not close pool on session close, since they are shared now""" |
507 self.rollback() |
507 self.rollback() |
508 |
508 |
509 def user_data(self): |
509 def user_data(self): |
510 """returns a dictionnary with this user's information""" |
510 """returns a dictionnary with this user's information""" |
511 return self.parent_session.user_data() |
511 return self.parent_session.user_data() |
512 |
512 |
513 |
513 |
514 class InternalSession(Session): |
514 class InternalSession(Session): |
515 """special session created internaly by the repository""" |
515 """special session created internaly by the repository""" |
516 |
516 |
517 def __init__(self, repo, cnxprops=None): |
517 def __init__(self, repo, cnxprops=None): |
518 super(InternalSession, self).__init__(_IMANAGER, repo, cnxprops, |
518 super(InternalSession, self).__init__(_IMANAGER, repo, cnxprops, |
519 _id='internal') |
519 _id='internal') |
520 self.cnxtype = 'inmemory' |
520 self.cnxtype = 'inmemory' |
521 self.is_internal_session = True |
521 self.is_internal_session = True |
522 self.is_super_session = True |
522 self.is_super_session = True |
523 |
523 |
524 @property |
524 @property |
525 def super_session(self): |
525 def super_session(self): |
526 return self |
526 return self |
527 |
527 |
528 |
528 |