summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy
diff options
context:
space:
mode:
Diffstat (limited to 'lib/sqlalchemy')
-rw-r--r--lib/sqlalchemy/ext/sessioncontext.py5
-rw-r--r--lib/sqlalchemy/orm/__init__.py26
-rw-r--r--lib/sqlalchemy/orm/interfaces.py9
-rw-r--r--lib/sqlalchemy/orm/mapper.py6
-rw-r--r--lib/sqlalchemy/orm/session.py95
-rw-r--r--lib/sqlalchemy/orm/sessionmaker.py94
-rw-r--r--lib/sqlalchemy/orm/util.py1
-rw-r--r--lib/sqlalchemy/util.py5
8 files changed, 210 insertions, 31 deletions
diff --git a/lib/sqlalchemy/ext/sessioncontext.py b/lib/sqlalchemy/ext/sessioncontext.py
index 91c03d3c3..8221bc495 100644
--- a/lib/sqlalchemy/ext/sessioncontext.py
+++ b/lib/sqlalchemy/ext/sessioncontext.py
@@ -1,4 +1,4 @@
-from sqlalchemy.util import ScopedRegistry
+from sqlalchemy.util import ScopedRegistry, warn_deprecated
from sqlalchemy.orm import create_session, object_session, MapperExtension, EXT_CONTINUE
__all__ = ['SessionContext', 'SessionContextExt']
@@ -25,6 +25,7 @@ class SessionContext(object):
"""
def __init__(self, session_factory=None, scopefunc=None):
+ warn_deprecated("SessionContext is deprecated. Use Session=sessionmaker(scope='thread').")
if session_factory is None:
session_factory = create_session
self.registry = ScopedRegistry(session_factory, scopefunc)
@@ -63,7 +64,7 @@ class SessionContextExt(MapperExtension):
def get_session(self):
return self.context.current
- def init_instance(self, mapper, class_, instance, args, kwargs):
+ def init_instance(self, mapper, class_, oldinit, instance, args, kwargs):
session = kwargs.pop('_sa_session', self.context.current)
session._save_impl(instance, entity_name=kwargs.pop('_sa_entity_name', None))
return EXT_CONTINUE
diff --git a/lib/sqlalchemy/orm/__init__.py b/lib/sqlalchemy/orm/__init__.py
index 3c221bb66..26b583bb1 100644
--- a/lib/sqlalchemy/orm/__init__.py
+++ b/lib/sqlalchemy/orm/__init__.py
@@ -20,14 +20,15 @@ from sqlalchemy.orm import collections, strategies
from sqlalchemy.orm.query import Query
from sqlalchemy.orm.util import polymorphic_union
from sqlalchemy.orm.session import Session as _Session
-from sqlalchemy.orm.session import object_session, attribute_manager
+from sqlalchemy.orm.session import object_session, attribute_manager, sessionmaker
+from sqlalchemy.orm.sessionmaker import sessionmaker
__all__ = [ 'relation', 'column_property', 'composite', 'backref', 'eagerload',
'eagerload_all', 'lazyload', 'noload', 'deferred', 'defer',
'undefer', 'undefer_group', 'extension', 'mapper', 'clear_mappers',
- 'compile_mappers', 'class_mapper', 'object_mapper',
+ 'compile_mappers', 'class_mapper', 'object_mapper', 'sessionmaker',
'dynamic_loader', 'MapperExtension', 'Query', 'polymorphic_union',
- 'create_session', 'Session', 'synonym', 'contains_alias',
+ 'create_session', 'synonym', 'contains_alias',
'contains_eager', 'EXT_CONTINUE', 'EXT_STOP', 'EXT_PASS',
'object_session', 'PropComparator' ]
@@ -38,28 +39,11 @@ def create_session(bind=None, **kwargs):
The session by default does not begin a transaction, and requires that
flush() be called explicitly in order to persist results to the database.
"""
-
+ sautil.warn_deprecated("create_session() is deprecated. Use Session=sessionmaker() instead.")
kwargs.setdefault('autoflush', False)
kwargs.setdefault('transactional', False)
return _Session(bind=bind, **kwargs)
-class Session(_Session):
- """front-end for a [sqlalchemy.orm.session#Session]. By default,
- produces an autoflushing, transactional session."""
-
- def __init__(self, bind=None, **kwargs):
- """create a new transactional [sqlalchemy.orm.session#Session].
-
- The session starts a new transaction for each database accessed. To
- commit the transaction, use the commit() method. SQL is issued for
- write operations (i.e. flushes) automatically in most cases, before each query
- and during commit.
- """
-
- kwargs.setdefault('autoflush', True)
- kwargs.setdefault('transactional', True)
- super(Session, self).__init__(bind=bind, **kwargs)
-
def relation(argument, secondary=None, **kwargs):
"""Provide a relationship of a primary Mapper to a secondary Mapper.
diff --git a/lib/sqlalchemy/orm/interfaces.py b/lib/sqlalchemy/orm/interfaces.py
index 271846ca8..abaeff49c 100644
--- a/lib/sqlalchemy/orm/interfaces.py
+++ b/lib/sqlalchemy/orm/interfaces.py
@@ -34,11 +34,14 @@ class MapperExtension(object):
EXT_PASS is a synonym for EXT_CONTINUE and is provided for
backward compatibility.
"""
-
- def init_instance(self, mapper, class_, instance, args, kwargs):
+
+ def instrument_class(self, mapper, class_):
+ return EXT_CONTINUE
+
+ def init_instance(self, mapper, class_, oldinit, instance, args, kwargs):
return EXT_CONTINUE
- def init_failed(self, mapper, class_, instance, args, kwargs):
+ def init_failed(self, mapper, class_, oldinit, instance, args, kwargs):
return EXT_CONTINUE
def get_session(self):
diff --git a/lib/sqlalchemy/orm/mapper.py b/lib/sqlalchemy/orm/mapper.py
index debce60f2..af7c9d4cf 100644
--- a/lib/sqlalchemy/orm/mapper.py
+++ b/lib/sqlalchemy/orm/mapper.py
@@ -304,6 +304,8 @@ class Mapper(object):
for ext in extlist:
self.extension.append(ext)
+ self.extension.instrument_class(self, self.class_)
+
def _compile_inheritance(self):
"""Determine if this Mapper inherits from another mapper, and
if so calculates the mapped_table for this Mapper taking the
@@ -679,7 +681,7 @@ class Mapper(object):
oldinit = self.class_.__init__
def init(instance, *args, **kwargs):
self.compile()
- self.extension.init_instance(self, self.class_, instance, args, kwargs)
+ self.extension.init_instance(self, self.class_, oldinit, instance, args, kwargs)
if oldinit is not None:
try:
@@ -687,7 +689,7 @@ class Mapper(object):
except:
# call init_failed but suppress exceptions into warnings so that original __init__
# exception is raised
- util.warn_exception(self.extension.init_failed, self, self.class_, instance, args, kwargs)
+ util.warn_exception(self.extension.init_failed, self, self.class_, oldinit, instance, args, kwargs)
raise
# override oldinit, ensuring that its not already a Mapper-decorated init method
diff --git a/lib/sqlalchemy/orm/session.py b/lib/sqlalchemy/orm/session.py
index b957f822f..996c7d8a0 100644
--- a/lib/sqlalchemy/orm/session.py
+++ b/lib/sqlalchemy/orm/session.py
@@ -4,13 +4,104 @@
# This module is part of SQLAlchemy and is released under
# the MIT License: http://www.opensource.org/licenses/mit-license.php
-import weakref
+import weakref, types
from sqlalchemy import util, exceptions, sql, engine
-from sqlalchemy.orm import unitofwork, query, util as mapperutil
+from sqlalchemy.orm import unitofwork, query, util as mapperutil, MapperExtension, EXT_CONTINUE
from sqlalchemy.orm.mapper import object_mapper as _object_mapper
from sqlalchemy.orm.mapper import class_mapper as _class_mapper
+from sqlalchemy.orm.mapper import global_extensions
+__all__ = ['Session', 'SessionTransaction']
+
+def sessionmaker(autoflush, transactional, bind=None, scope=None, enhance_classes=False, **kwargs):
+ """Generate a Session configuration."""
+
+ if enhance_classes and scope is None:
+ raise exceptions.InvalidRequestError("enhance_classes requires a non-None 'scope' argument, so that mappers can automatically locate a Session already in progress.")
+
+ class Sess(Session):
+ def __init__(self, **local_kwargs):
+ local_kwargs.setdefault('bind', bind)
+ local_kwargs.setdefault('autoflush', autoflush)
+ local_kwargs.setdefault('transactional', transactional)
+ for k in kwargs:
+ local_kwargs.setdefault(k, kwargs[k])
+ super(Sess, self).__init__(**local_kwargs)
+
+ if scope=="thread":
+ registry = util.ScopedRegistry(Sess, scopefunc=None)
+
+ if enhance_classes:
+ class SessionContextExt(MapperExtension):
+ def get_session(self):
+ return registry()
+
+ def instrument_class(self, mapper, class_):
+ class query(object):
+ def __getattr__(self, key):
+ return getattr(registry().query(class_), key)
+ def __call__(self):
+ return registry().query(class_)
+
+ if not hasattr(class_, 'query'):
+ class_.query = query()
+
+ def init_instance(self, mapper, class_, oldinit, instance, args, kwargs):
+ session = kwargs.pop('_sa_session', registry())
+ if not isinstance(oldinit, types.MethodType):
+ for key, value in kwargs.items():
+ #if validate:
+ # if not self.mapper.get_property(key, resolve_synonyms=False, raiseerr=False):
+ # raise exceptions.ArgumentError("Invalid __init__ argument: '%s'" % key)
+ setattr(instance, key, value)
+ session._save_impl(instance, entity_name=kwargs.pop('_sa_entity_name', None))
+ return EXT_CONTINUE
+
+ def init_failed(self, mapper, class_, oldinit, instance, args, kwargs):
+ object_session(instance).expunge(instance)
+ return EXT_CONTINUE
+
+ def dispose_class(self, mapper, class_):
+ if hasattr(class_, '__init__') and hasattr(class_.__init__, '_oldinit'):
+ if class_.__init__._oldinit is not None:
+ class_.__init__ = class_.__init__._oldinit
+ else:
+ delattr(class_, '__init__')
+ if hasattr(class_, 'query'):
+ delattr(class_, 'query')
+
+ global_extensions.append(SessionContextExt())
+
+ default_scope=scope
+ class ScopedSess(Sess):
+ def __new__(cls, **kwargs):
+ if len(kwargs):
+ scope = kwargs.pop('scope', default_scope)
+ if scope is not None:
+ if registry.has():
+ raise exceptions.InvalidRequestError("Scoped session is already present; no new arguments may be specified.")
+ else:
+ sess = Sess(**kwargs)
+ registry.set(sess)
+ return sess
+ else:
+ return Sess(**kwargs)
+ else:
+ return registry()
+ def instrument(name):
+ def do(cls, *args, **kwargs):
+ return getattr(registry(), name)(*args, **kwargs)
+ return classmethod(do)
+ for meth in ('get', 'close', 'save', 'commit', 'update', 'flush', 'query', 'delete'):
+ setattr(ScopedSess, meth, instrument(meth))
+
+ return ScopedSess
+ elif scope is not None:
+ raise exceptions.ArgumentError("Unknown scope '%s'" % scope)
+ else:
+ return session
+
class SessionTransaction(object):
"""Represents a Session-level Transaction.
diff --git a/lib/sqlalchemy/orm/sessionmaker.py b/lib/sqlalchemy/orm/sessionmaker.py
new file mode 100644
index 000000000..42064c5b8
--- /dev/null
+++ b/lib/sqlalchemy/orm/sessionmaker.py
@@ -0,0 +1,94 @@
+import types
+
+from sqlalchemy import util, exceptions
+from sqlalchemy.orm.session import Session
+from sqlalchemy.orm import query, util as mapperutil, MapperExtension, EXT_CONTINUE
+from sqlalchemy.orm.mapper import global_extensions
+
+def sessionmaker(autoflush, transactional, bind=None, scope=None, enhance_classes=False, **kwargs):
+ """Generate a Session configuration."""
+
+ if enhance_classes and scope is None:
+ raise exceptions.InvalidRequestError("enhance_classes requires a non-None 'scope' argument, so that mappers can automatically locate a Session already in progress.")
+
+ class Sess(Session):
+ def __init__(self, **local_kwargs):
+ local_kwargs.setdefault('bind', bind)
+ local_kwargs.setdefault('autoflush', autoflush)
+ local_kwargs.setdefault('transactional', transactional)
+ for k in kwargs:
+ local_kwargs.setdefault(k, kwargs[k])
+ super(Sess, self).__init__(**local_kwargs)
+
+ if scope=="thread":
+ registry = util.ScopedRegistry(Sess, scopefunc=None)
+
+ if enhance_classes:
+ class SessionContextExt(MapperExtension):
+ def get_session(self):
+ return registry()
+
+ def instrument_class(self, mapper, class_):
+ class query(object):
+ def __getattr__(self, key):
+ return getattr(registry().query(class_), key)
+ def __call__(self):
+ return registry().query(class_)
+
+ if not hasattr(class_, 'query'):
+ class_.query = query()
+
+ def init_instance(self, mapper, class_, oldinit, instance, args, kwargs):
+ session = kwargs.pop('_sa_session', registry())
+ if not isinstance(oldinit, types.MethodType):
+ for key, value in kwargs.items():
+ #if validate:
+ # if not self.mapper.get_property(key, resolve_synonyms=False, raiseerr=False):
+ # raise exceptions.ArgumentError("Invalid __init__ argument: '%s'" % key)
+ setattr(instance, key, value)
+ session._save_impl(instance, entity_name=kwargs.pop('_sa_entity_name', None))
+ return EXT_CONTINUE
+
+ def init_failed(self, mapper, class_, oldinit, instance, args, kwargs):
+ object_session(instance).expunge(instance)
+ return EXT_CONTINUE
+
+ def dispose_class(self, mapper, class_):
+ if hasattr(class_, '__init__') and hasattr(class_.__init__, '_oldinit'):
+ if class_.__init__._oldinit is not None:
+ class_.__init__ = class_.__init__._oldinit
+ else:
+ delattr(class_, '__init__')
+ if hasattr(class_, 'query'):
+ delattr(class_, 'query')
+
+ global_extensions.append(SessionContextExt())
+
+ default_scope=scope
+ class ScopedSess(Sess):
+ def __new__(cls, **kwargs):
+ if len(kwargs):
+ scope = kwargs.pop('scope', default_scope)
+ if scope is not None:
+ if registry.has():
+ raise exceptions.InvalidRequestError("Scoped session is already present; no new arguments may be specified.")
+ else:
+ sess = Sess(**kwargs)
+ registry.set(sess)
+ return sess
+ else:
+ return Sess(**kwargs)
+ else:
+ return registry()
+ def instrument(name):
+ def do(cls, *args, **kwargs):
+ return getattr(registry(), name)(*args, **kwargs)
+ return classmethod(do)
+ for meth in ('get', 'close', 'save', 'commit', 'update', 'flush', 'query', 'delete'):
+ setattr(ScopedSess, meth, instrument(meth))
+
+ return ScopedSess
+ elif scope is not None:
+ raise exceptions.ArgumentError("Unknown scope '%s'" % scope)
+ else:
+ return Sess
diff --git a/lib/sqlalchemy/orm/util.py b/lib/sqlalchemy/orm/util.py
index 6a9c4164f..727be50c6 100644
--- a/lib/sqlalchemy/orm/util.py
+++ b/lib/sqlalchemy/orm/util.py
@@ -140,6 +140,7 @@ class ExtensionCarrier(MapperExtension):
return EXT_CONTINUE
return _do
+ instrument_class = _create_do('instrument_class')
init_instance = _create_do('init_instance')
init_failed = _create_do('init_failed')
dispose_class = _create_do('dispose_class')
diff --git a/lib/sqlalchemy/util.py b/lib/sqlalchemy/util.py
index 7391d86fd..ea1c8286a 100644
--- a/lib/sqlalchemy/util.py
+++ b/lib/sqlalchemy/util.py
@@ -543,7 +543,10 @@ class ScopedRegistry(object):
return self.registry[key]
except KeyError:
return self.registry.setdefault(key, self.createfunc())
-
+
+ def has(self):
+ return self._get_key() in self.registry
+
def set(self, obj):
self.registry[self._get_key()] = obj