summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/sqlalchemy/engine/reflection.py39
-rw-r--r--lib/sqlalchemy/exc.py2
-rw-r--r--lib/sqlalchemy/inspection.py48
-rw-r--r--lib/sqlalchemy/orm/attributes.py202
-rw-r--r--lib/sqlalchemy/orm/state.py70
-rw-r--r--lib/sqlalchemy/orm/util.py59
-rw-r--r--lib/sqlalchemy/util/langhelpers.py29
7 files changed, 307 insertions, 142 deletions
diff --git a/lib/sqlalchemy/engine/reflection.py b/lib/sqlalchemy/engine/reflection.py
index 3a12819f1..e1d702146 100644
--- a/lib/sqlalchemy/engine/reflection.py
+++ b/lib/sqlalchemy/engine/reflection.py
@@ -40,8 +40,8 @@ def cache(fn, self, con, *args, **kw):
if info_cache is None:
return fn(self, con, *args, **kw)
key = (
- fn.__name__,
- tuple(a for a in args if isinstance(a, basestring)),
+ fn.__name__,
+ tuple(a for a in args if isinstance(a, basestring)),
tuple((k, v) for k, v in kw.iteritems() if isinstance(v, (basestring, int, float)))
)
ret = info_cache.get(key)
@@ -59,8 +59,15 @@ class Inspector(object):
consistent interface as well as caching support for previously
fetched metadata.
- The preferred method to construct an :class:`.Inspector` is via the
- :meth:`Inspector.from_engine` method. I.e.::
+ A :class:`.Inspector` object is usually created via the
+ :func:`.inspect` function::
+
+ from sqlalchemy import inspect, create_engine
+ engine = create_engine('...')
+ insp = inspect(engine)
+
+ The inspection method above is equivalent to using the
+ :meth:`.Inspector.from_engine` method, i.e.::
engine = create_engine('...')
insp = Inspector.from_engine(engine)
@@ -74,9 +81,9 @@ class Inspector(object):
def __init__(self, bind):
"""Initialize a new :class:`.Inspector`.
- :param bind: a :class:`~sqlalchemy.engine.base.Connectable`,
- which is typically an instance of
- :class:`~sqlalchemy.engine.base.Engine` or
+ :param bind: a :class:`~sqlalchemy.engine.base.Connectable`,
+ which is typically an instance of
+ :class:`~sqlalchemy.engine.base.Engine` or
:class:`~sqlalchemy.engine.base.Connection`.
For a dialect-specific instance of :class:`.Inspector`, see
@@ -103,9 +110,9 @@ class Inspector(object):
def from_engine(cls, bind):
"""Construct a new dialect-specific Inspector object from the given engine or connection.
- :param bind: a :class:`~sqlalchemy.engine.base.Connectable`,
- which is typically an instance of
- :class:`~sqlalchemy.engine.base.Engine` or
+ :param bind: a :class:`~sqlalchemy.engine.base.Connectable`,
+ which is typically an instance of
+ :class:`~sqlalchemy.engine.base.Engine` or
:class:`~sqlalchemy.engine.base.Connection`.
This method differs from direct a direct constructor call of :class:`.Inspector`
@@ -322,7 +329,7 @@ class Inspector(object):
def reflecttable(self, table, include_columns, exclude_columns=()):
"""Given a Table object, load its internal constructs based on introspection.
- This is the underlying method used by most dialects to produce
+ This is the underlying method used by most dialects to produce
table reflection. Direct usage is like::
from sqlalchemy import create_engine, MetaData, Table
@@ -416,11 +423,11 @@ class Inspector(object):
# Primary keys
pk_cons = self.get_pk_constraint(table_name, schema, **tblkw)
if pk_cons:
- pk_cols = [table.c[pk]
- for pk in pk_cons['constrained_columns']
+ pk_cols = [table.c[pk]
+ for pk in pk_cons['constrained_columns']
if pk in table.c and pk not in exclude_columns
] + [pk for pk in table.primary_key if pk.key in exclude_columns]
- primary_key_constraint = sa_schema.PrimaryKeyConstraint(name=pk_cons.get('name'),
+ primary_key_constraint = sa_schema.PrimaryKeyConstraint(name=pk_cons.get('name'),
*pk_cols
)
@@ -457,7 +464,7 @@ class Inspector(object):
table.append_constraint(
sa_schema.ForeignKeyConstraint(constrained_columns, refspec,
conname, link_to_name=True))
- # Indexes
+ # Indexes
indexes = self.get_indexes(table_name, schema)
for index_d in indexes:
name = index_d['name']
@@ -470,5 +477,5 @@ class Inspector(object):
"Omitting %s KEY for (%s), key covers omitted columns." %
(flavor, ', '.join(columns)))
continue
- sa_schema.Index(name, *[table.columns[c] for c in columns],
+ sa_schema.Index(name, *[table.columns[c] for c in columns],
**dict(unique=unique))
diff --git a/lib/sqlalchemy/exc.py b/lib/sqlalchemy/exc.py
index 0d69795d1..f9f97718c 100644
--- a/lib/sqlalchemy/exc.py
+++ b/lib/sqlalchemy/exc.py
@@ -96,7 +96,7 @@ class InvalidRequestError(SQLAlchemyError):
"""
class NoInspectionAvailable(InvalidRequestError):
- """A class to :func:`sqlalchemy.inspection.inspect` produced
+ """A subject passed to :func:`sqlalchemy.inspection.inspect` produced
no context for inspection."""
class ResourceClosedError(InvalidRequestError):
diff --git a/lib/sqlalchemy/inspection.py b/lib/sqlalchemy/inspection.py
index 8b0a751ad..34a47217b 100644
--- a/lib/sqlalchemy/inspection.py
+++ b/lib/sqlalchemy/inspection.py
@@ -4,22 +4,54 @@
# This module is part of SQLAlchemy and is released under
# the MIT License: http://www.opensource.org/licenses/mit-license.php
-"""Base inspect API.
+"""The inspection module provides the :func:`.inspect` function,
+which delivers runtime information about a wide variety
+of SQLAlchemy objects, both within the Core as well as the
+ORM.
-:func:`.inspect` provides access to a contextual object
-regarding a subject.
+The :func:`.inspect` function is the entry point to SQLAlchemy's
+public API for viewing the configuration and construction
+of in-memory objects. Depending on the type of object
+passed to :func:`.inspect`, the return value will either be
+a related object which provides a known interface, or in many
+cases it will return the object itself.
+
+The rationale for :func:`.inspect` is twofold. One is that
+it replaces the need to be aware of a large variety of "information
+getting" functions in SQLAlchemy, such as :meth:`.Inspector.from_engine`,
+:func:`.orm.attributes.instance_state`, :func:`.orm.class_mapper`,
+and others. The other is that the return value of :func:`.inspect`
+is guaranteed to obey a documented API, thus allowing third party
+tools which build on top of SQLAlchemy configurations to be constructed
+in a forwards-compatible way.
+
+.. versionadded:: 0.8 The :func:`.inspect` system is introduced
+ as of version 0.8.
-Various subsections of SQLAlchemy,
-such as the :class:`.Inspector`, :class:`.Mapper`, and
-others register themselves with the "inspection registry" here
-so that they may return a context object given a certain kind
-of argument.
"""
from . import util, exc
_registrars = util.defaultdict(list)
def inspect(subject, raiseerr=True):
+ """Produce an inspection object for the given target.
+
+ The returned value in some cases may be the
+ same object as the one given, such as if a
+ :class:`.orm.Mapper` object is passed. In other
+ cases, it will be an instance of the registered
+ inspection type for the given object, such as
+ if a :class:`.engine.Engine` is passed, an
+ :class:`.engine.Inspector` object is returned.
+
+ :param subject: the subject to be inspected.
+ :param raiseerr: When ``True``, if the given subject
+ does not
+ correspond to a known SQLAlchemy inspected type,
+ :class:`sqlalchemy.exc.NoInspectionAvailable`
+ is raised. If ``False``, ``None`` is returned.
+
+ """
type_ = type(subject)
for cls in type_.__mro__:
if cls in _registrars:
diff --git a/lib/sqlalchemy/orm/attributes.py b/lib/sqlalchemy/orm/attributes.py
index 0bf9ea438..9a1c60aa7 100644
--- a/lib/sqlalchemy/orm/attributes.py
+++ b/lib/sqlalchemy/orm/attributes.py
@@ -52,7 +52,7 @@ indicating that the attribute had not been assigned to previously.
"""
)
-NO_CHANGE = util.symbol("NO_CHANGE",
+NO_CHANGE = util.symbol("NO_CHANGE",
"""No callables or SQL should be emitted on attribute access
and no state should change""", canonical=0
)
@@ -80,29 +80,42 @@ value can be obtained.
)
NON_PERSISTENT_OK = util.symbol("NON_PERSISTENT_OK",
-"""callables can be emitted if the parent is not persistent.""",
+"""callables can be emitted if the parent is not persistent.""",
canonical=16
)
-
# pre-packaged sets of flags used as inputs
-PASSIVE_OFF = RELATED_OBJECT_OK | \
- NON_PERSISTENT_OK | \
- INIT_OK | \
- CALLABLES_OK | \
- SQL_OK
-
-PASSIVE_RETURN_NEVER_SET = PASSIVE_OFF ^ INIT_OK
-PASSIVE_NO_INITIALIZE = PASSIVE_RETURN_NEVER_SET ^ CALLABLES_OK
-PASSIVE_NO_FETCH = PASSIVE_OFF ^ SQL_OK
-PASSIVE_NO_FETCH_RELATED = PASSIVE_OFF ^ RELATED_OBJECT_OK
-PASSIVE_ONLY_PERSISTENT = PASSIVE_OFF ^ NON_PERSISTENT_OK
+PASSIVE_OFF = util.symbol("PASSIVE_OFF",
+ "Callables can be emitted in all cases.",
+ canonical=(RELATED_OBJECT_OK | NON_PERSISTENT_OK |
+ INIT_OK | CALLABLES_OK | SQL_OK)
+)
+PASSIVE_RETURN_NEVER_SET = util.symbol("PASSIVE_RETURN_NEVER_SET",
+ """PASSIVE_OFF ^ INIT_OK""",
+ canonical=PASSIVE_OFF ^ INIT_OK
+)
+PASSIVE_NO_INITIALIZE = util.symbol("PASSIVE_NO_INITIALIZE",
+ "PASSIVE_RETURN_NEVER_SET ^ CALLABLES_OK",
+ canonical=PASSIVE_RETURN_NEVER_SET ^ CALLABLES_OK
+)
+PASSIVE_NO_FETCH = util.symbol("PASSIVE_NO_FETCH",
+ "PASSIVE_OFF ^ SQL_OK",
+ canonical=PASSIVE_OFF ^ SQL_OK
+)
+PASSIVE_NO_FETCH_RELATED = util.symbol("PASSIVE_NO_FETCH_RELATED",
+ "PASSIVE_OFF ^ RELATED_OBJECT_OK",
+ canonical=PASSIVE_OFF ^ RELATED_OBJECT_OK
+)
+PASSIVE_ONLY_PERSISTENT = util.symbol("PASSIVE_ONLY_PERSISTENT",
+ "PASSIVE_OFF ^ NON_PERSISTENT_OK",
+ canonical=PASSIVE_OFF ^ NON_PERSISTENT_OK
+)
class QueryableAttribute(interfaces.PropComparator):
"""Base class for class-bound attributes. """
- def __init__(self, class_, key, impl=None,
+ def __init__(self, class_, key, impl=None,
comparator=None, parententity=None,
of_type=None):
self.class_ = class_
@@ -115,7 +128,7 @@ class QueryableAttribute(interfaces.PropComparator):
manager = manager_of_class(class_)
# manager is None in the case of AliasedClass
if manager:
- # propagate existing event listeners from
+ # propagate existing event listeners from
# immediate superclass
for base in manager._bases:
if key in base:
@@ -166,8 +179,8 @@ class QueryableAttribute(interfaces.PropComparator):
except AttributeError:
raise AttributeError(
'Neither %r object nor %r object has an attribute %r' % (
- type(self).__name__,
- type(self.comparator).__name__,
+ type(self).__name__,
+ type(self.comparator).__name__,
key)
)
@@ -187,7 +200,7 @@ class InstrumentedAttribute(QueryableAttribute):
"""Class bound instrumented attribute which adds descriptor methods."""
def __set__(self, instance, value):
- self.impl.set(instance_state(instance),
+ self.impl.set(instance_state(instance),
instance_dict(instance), value, None)
def __delete__(self, instance):
@@ -215,13 +228,13 @@ def create_proxied_attribute(descriptor):
class Proxy(QueryableAttribute):
"""Presents the :class:`.QueryableAttribute` interface as a
- proxy on top of a Python descriptor / :class:`.PropComparator`
+ proxy on top of a Python descriptor / :class:`.PropComparator`
combination.
"""
- def __init__(self, class_, key, descriptor,
- comparator,
+ def __init__(self, class_, key, descriptor,
+ comparator,
adapter=None, doc=None,
original_property=None):
self.class_ = class_
@@ -272,8 +285,8 @@ def create_proxied_attribute(descriptor):
except AttributeError:
raise AttributeError(
'Neither %r object nor %r object has an attribute %r' % (
- type(descriptor).__name__,
- type(self.comparator).__name__,
+ type(descriptor).__name__,
+ type(self.comparator).__name__,
attribute)
)
@@ -289,7 +302,7 @@ class AttributeImpl(object):
def __init__(self, class_, key,
callable_, dispatch, trackparent=False, extension=None,
- compare_function=None, active_history=False,
+ compare_function=None, active_history=False,
parent_token=None, expire_missing=True,
**kwargs):
"""Construct an AttributeImpl.
@@ -326,12 +339,12 @@ class AttributeImpl(object):
parent_token
Usually references the MapperProperty, used as a key for
the hasparent() function to identify an "owning" attribute.
- Allows multiple AttributeImpls to all match a single
+ Allows multiple AttributeImpls to all match a single
owner attribute.
expire_missing
if False, don't add an "expiry" callable to this attribute
- during state.expire_attributes(None), if no value is present
+ during state.expire_attributes(None), if no value is present
for this key.
"""
@@ -370,7 +383,7 @@ class AttributeImpl(object):
def hasparent(self, state, optimistic=False):
- """Return the boolean value of a `hasparent` flag attached to
+ """Return the boolean value of a `hasparent` flag attached to
the given state.
The `optimistic` flag determines what the default return value
@@ -414,8 +427,8 @@ class AttributeImpl(object):
"state %s along attribute '%s', "
"but the parent record "
"has gone stale, can't be sure this "
- "is the most recent parent." %
- (orm_util.state_str(state),
+ "is the most recent parent." %
+ (orm_util.state_str(state),
orm_util.state_str(parent_state),
self.key))
@@ -445,8 +458,8 @@ class AttributeImpl(object):
raise NotImplementedError()
def get_all_pending(self, state, dict_):
- """Return a list of tuples of (state, obj)
- for all objects in this attribute's current state
+ """Return a list of tuples of (state, obj)
+ for all objects in this attribute's current state
+ history.
Only applies to object-based attributes.
@@ -455,8 +468,8 @@ class AttributeImpl(object):
which roughly corresponds to:
get_state_history(
- state,
- key,
+ state,
+ key,
passive=PASSIVE_NO_INITIALIZE).sum()
"""
@@ -517,14 +530,14 @@ class AttributeImpl(object):
self.set(state, dict_, value, initiator, passive=passive)
def remove(self, state, dict_, value, initiator, passive=PASSIVE_OFF):
- self.set(state, dict_, None, initiator,
+ self.set(state, dict_, None, initiator,
passive=passive, check_old=value)
def pop(self, state, dict_, value, initiator, passive=PASSIVE_OFF):
- self.set(state, dict_, None, initiator,
+ self.set(state, dict_, None, initiator,
passive=passive, check_old=value, pop=True)
- def set(self, state, dict_, value, initiator,
+ def set(self, state, dict_, value, initiator,
passive=PASSIVE_OFF, check_old=None, pop=False):
raise NotImplementedError()
@@ -571,7 +584,7 @@ class ScalarAttributeImpl(AttributeImpl):
return History.from_scalar_attribute(
self, state, dict_.get(self.key, NO_VALUE))
- def set(self, state, dict_, value, initiator,
+ def set(self, state, dict_, value, initiator,
passive=PASSIVE_OFF, check_old=None, pop=False):
if initiator and initiator.parent_token is self.parent_token:
return
@@ -582,7 +595,7 @@ class ScalarAttributeImpl(AttributeImpl):
old = dict_.get(self.key, NO_VALUE)
if self.dispatch.set:
- value = self.fire_replace_event(state, dict_,
+ value = self.fire_replace_event(state, dict_,
value, old, initiator)
state._modified_event(dict_, self, old)
dict_[self.key] = value
@@ -604,7 +617,7 @@ class ScalarAttributeImpl(AttributeImpl):
class ScalarObjectAttributeImpl(ScalarAttributeImpl):
- """represents a scalar-holding InstrumentedAttribute,
+ """represents a scalar-holding InstrumentedAttribute,
where the target object is also instrumented.
Adds events to delete/set operations.
@@ -650,7 +663,7 @@ class ScalarObjectAttributeImpl(ScalarAttributeImpl):
else:
return []
- def set(self, state, dict_, value, initiator,
+ def set(self, state, dict_, value, initiator,
passive=PASSIVE_OFF, check_old=None, pop=False):
"""Set a value on the given InstanceState.
@@ -729,12 +742,12 @@ class CollectionAttributeImpl(AttributeImpl):
typecallable=None, trackparent=False, extension=None,
copy_function=None, compare_function=None, **kwargs):
super(CollectionAttributeImpl, self).__init__(
- class_,
- key,
+ class_,
+ key,
callable_, dispatch,
trackparent=trackparent,
extension=extension,
- compare_function=compare_function,
+ compare_function=compare_function,
**kwargs)
if copy_function is None:
@@ -762,11 +775,11 @@ class CollectionAttributeImpl(AttributeImpl):
if self.key in state.committed_state:
original = state.committed_state[self.key]
if original is not NO_VALUE:
- current_states = [((c is not None) and
- instance_state(c) or None, c)
+ current_states = [((c is not None) and
+ instance_state(c) or None, c)
for c in current]
- original_states = [((c is not None) and
- instance_state(c) or None, c)
+ original_states = [((c is not None) and
+ instance_state(c) or None, c)
for c in original]
current_set = dict(current_states)
@@ -853,13 +866,13 @@ class CollectionAttributeImpl(AttributeImpl):
def pop(self, state, dict_, value, initiator, passive=PASSIVE_OFF):
try:
# TODO: better solution here would be to add
- # a "popper" role to collections.py to complement
+ # a "popper" role to collections.py to complement
# "remover".
self.remove(state, dict_, value, initiator, passive=passive)
except (ValueError, KeyError, IndexError):
pass
- def set(self, state, dict_, value, initiator,
+ def set(self, state, dict_, value, initiator,
passive=PASSIVE_OFF, pop=False):
"""Set a value on the given object.
@@ -938,7 +951,7 @@ class CollectionAttributeImpl(AttributeImpl):
return user_data
- def get_collection(self, state, dict_,
+ def get_collection(self, state, dict_,
user_data=None, passive=PASSIVE_OFF):
"""Retrieve the CollectionAdapter associated with the given state.
@@ -967,19 +980,19 @@ def backref_listeners(attribute, key, uselist):
old_state, old_dict = instance_state(oldchild),\
instance_dict(oldchild)
impl = old_state.manager[key].impl
- impl.pop(old_state,
- old_dict,
- state.obj(),
+ impl.pop(old_state,
+ old_dict,
+ state.obj(),
initiator, passive=PASSIVE_NO_FETCH)
if child is not None:
child_state, child_dict = instance_state(child),\
instance_dict(child)
child_state.manager[key].impl.append(
- child_state,
- child_dict,
- state.obj(),
- initiator,
+ child_state,
+ child_dict,
+ state.obj(),
+ initiator,
passive=PASSIVE_NO_FETCH)
return child
@@ -987,10 +1000,10 @@ def backref_listeners(attribute, key, uselist):
child_state, child_dict = instance_state(child), \
instance_dict(child)
child_state.manager[key].impl.append(
- child_state,
- child_dict,
- state.obj(),
- initiator,
+ child_state,
+ child_dict,
+ state.obj(),
+ initiator,
passive=PASSIVE_NO_FETCH)
return child
@@ -999,29 +1012,29 @@ def backref_listeners(attribute, key, uselist):
child_state, child_dict = instance_state(child),\
instance_dict(child)
child_state.manager[key].impl.pop(
- child_state,
- child_dict,
- state.obj(),
+ child_state,
+ child_dict,
+ state.obj(),
initiator,
passive=PASSIVE_NO_FETCH)
if uselist:
- event.listen(attribute, "append",
- emit_backref_from_collection_append_event,
+ event.listen(attribute, "append",
+ emit_backref_from_collection_append_event,
retval=True, raw=True)
else:
- event.listen(attribute, "set",
- emit_backref_from_scalar_set_event,
+ event.listen(attribute, "set",
+ emit_backref_from_scalar_set_event,
retval=True, raw=True)
# TODO: need coverage in test/orm/ of remove event
- event.listen(attribute, "remove",
- emit_backref_from_collection_remove_event,
+ event.listen(attribute, "remove",
+ emit_backref_from_collection_remove_event,
retval=True, raw=True)
_NO_HISTORY = util.symbol('NO_HISTORY')
_NO_STATE_SYMBOLS = frozenset([
- id(PASSIVE_NO_RESULT),
- id(NO_VALUE),
+ id(PASSIVE_NO_RESULT),
+ id(NO_VALUE),
id(NEVER_SET)])
class History(tuple):
"""A 3-tuple of added, unchanged and deleted values,
@@ -1062,7 +1075,7 @@ class History(tuple):
return not bool(
(self.added or self.deleted)
or self.unchanged and self.unchanged != [None]
- )
+ )
def sum(self):
"""Return a collection of added + unchanged + deleted."""
@@ -1114,7 +1127,7 @@ class History(tuple):
elif attribute.is_equal(current, original) is True:
return cls((), [current], ())
else:
- # current convention on native scalars is to not
+ # current convention on native scalars is to not
# include information
# about missing previous value in "deleted", but
# we do include None, which helps in some primary
@@ -1140,11 +1153,11 @@ class History(tuple):
elif current is original:
return cls((), [current], ())
else:
- # current convention on related objects is to not
+ # current convention on related objects is to not
# include information
# about missing previous value in "deleted", and
# to also not include None - the dependency.py rules
- # ignore the None in any case.
+ # ignore the None in any case.
if id(original) in _NO_STATE_SYMBOLS or original is None:
deleted = ()
else:
@@ -1165,11 +1178,11 @@ class History(tuple):
return cls((), list(current), ())
else:
- current_states = [((c is not None) and instance_state(c) or None, c)
- for c in current
+ current_states = [((c is not None) and instance_state(c) or None, c)
+ for c in current
]
- original_states = [((c is not None) and instance_state(c) or None, c)
- for c in original
+ original_states = [((c is not None) and instance_state(c) or None, c)
+ for c in original
]
current_set = dict(current_states)
@@ -1184,7 +1197,7 @@ class History(tuple):
HISTORY_BLANK = History(None, None, None)
def get_history(obj, key, passive=PASSIVE_OFF):
- """Return a :class:`.History` record for the given object
+ """Return a :class:`.History` record for the given object
and attribute key.
:param obj: an object whose class is instrumented by the
@@ -1192,10 +1205,11 @@ def get_history(obj, key, passive=PASSIVE_OFF):
:param key: string attribute name.
- :param passive: indicates if the attribute should be
- loaded from the database if not already present (:attr:`.PASSIVE_NO_FETCH`), and
- if the attribute should be not initialized to a blank value otherwise
- (:attr:`.PASSIVE_NO_INITIALIZE`). Default is :attr:`PASSIVE_OFF`.
+ :param passive: indicates loading behavior for the attribute
+ if the value is not already present. This is a
+ bitflag attribute, which defaults to the symbol
+ :attr:`.PASSIVE_OFF` indicating all necessary SQL
+ should be emitted.
"""
if passive is True:
@@ -1223,14 +1237,14 @@ def register_attribute(class_, key, **kw):
comparator = kw.pop('comparator', None)
parententity = kw.pop('parententity', None)
doc = kw.pop('doc', None)
- desc = register_descriptor(class_, key,
+ desc = register_descriptor(class_, key,
comparator, parententity, doc=doc)
register_attribute_impl(class_, key, **kw)
return desc
def register_attribute_impl(class_, key,
- uselist=False, callable_=None,
- useobject=False,
+ uselist=False, callable_=None,
+ useobject=False,
impl_class=None, backref=None, **kw):
manager = manager_of_class(class_)
@@ -1262,7 +1276,7 @@ def register_attribute_impl(class_, key,
manager.post_configure_attribute(key)
return manager[key]
-def register_descriptor(class_, key, comparator=None,
+def register_descriptor(class_, key, comparator=None,
parententity=None, doc=None):
manager = manager_of_class(class_)
@@ -1288,10 +1302,10 @@ def init_collection(obj, key):
collection_adapter.append_without_event(elem)
For an easier way to do the above, see
- :func:`~sqlalchemy.orm.attributes.set_committed_value`.
+ :func:`~sqlalchemy.orm.attributes.set_committed_value`.
obj is an instrumented object instance. An InstanceState
- is accepted directly for backwards compatibility but
+ is accepted directly for backwards compatibility but
this usage is deprecated.
"""
@@ -1309,7 +1323,7 @@ def init_state_collection(state, dict_, key):
def set_committed_value(instance, key, value):
"""Set the value of an attribute with no history events.
- Cancels any previous history present. The value should be
+ Cancels any previous history present. The value should be
a scalar value for scalar-holding attributes, or
an iterable for any collection-holding attribute.
@@ -1366,7 +1380,7 @@ def del_attribute(instance, key):
def flag_modified(instance, key):
"""Mark an attribute on an instance as 'modified'.
- This sets the 'modified' flag on the instance and
+ This sets the 'modified' flag on the instance and
establishes an unconditional change event for the given attribute.
"""
diff --git a/lib/sqlalchemy/orm/state.py b/lib/sqlalchemy/orm/state.py
index 2b846832e..17ffa9e7a 100644
--- a/lib/sqlalchemy/orm/state.py
+++ b/lib/sqlalchemy/orm/state.py
@@ -50,6 +50,13 @@ class InstanceState(interfaces._InspectionAttr):
@util.memoized_property
def attr(self):
+ """Return a namespace representing each attribute on
+ the mapped object, including its current value
+ and history.
+
+ The returned object is an instance of :class:`.InspectAttr`.
+
+ """
return util.ImmutableProperties(
dict(
(key, InspectAttr(self, key))
@@ -59,21 +66,25 @@ class InstanceState(interfaces._InspectionAttr):
@property
def transient(self):
+ """Return true if the object is transient."""
return self.key is None and \
not self._attached
@property
def pending(self):
+ """Return true if the object is pending."""
return self.key is None and \
self._attached
@property
def persistent(self):
+ """Return true if the object is persistent."""
return self.key is not None and \
self._attached
@property
def detached(self):
+ """Return true if the object is detached."""
return self.key is not None and \
not self._attached
@@ -84,14 +95,32 @@ class InstanceState(interfaces._InspectionAttr):
@property
def session(self):
+ """Return the owning :class:`.Session` for this instance,
+ or ``None`` if none available."""
+
return sessionlib._state_session(self)
@property
def object(self):
+ """Return the mapped object represented by this
+ :class:`.InstanceState`."""
return self.obj()
@property
def identity(self):
+ """Return the mapped identity of the mapped object.
+ This is the primary key identity as persisted by the ORM
+ which can always be passed directly to
+ :meth:`.Query.get`.
+
+ Returns ``None`` if the object has no primary key identity.
+
+ .. note::
+ An object which is transient or pending
+ does **not** have a mapped identity until it is flushed,
+ even if its attributes include primary key values.
+
+ """
if self.key is None:
return None
else:
@@ -99,6 +128,14 @@ class InstanceState(interfaces._InspectionAttr):
@property
def identity_key(self):
+ """Return the identity key for the mapped object.
+
+ This is the key used to locate the object within
+ the :attr:`.Session.identity_map` mapping. It contains
+ the identity as returned by :attr:`.identity` within it.
+
+
+ """
# TODO: just change .key to .identity_key across
# the board ? probably
return self.key
@@ -113,10 +150,17 @@ class InstanceState(interfaces._InspectionAttr):
@util.memoized_property
def mapper(self):
+ """Return the :class:`.Mapper` used for this mapepd object."""
return self.manager.mapper
@property
def has_identity(self):
+ """Return ``True`` if this object has an identity key.
+
+ This should always have the same value as the
+ expression ``state.persistent or state.detached``.
+
+ """
return bool(self.key)
def _detach(self):
@@ -465,7 +509,14 @@ class InstanceState(interfaces._InspectionAttr):
state._strong_obj = None
class InspectAttr(object):
- """Provide inspection interface to an object's state."""
+ """Provide an inspection interface corresponding
+ to a particular attribute on a particular mapped object.
+
+ The :class:`.InspectAttr` object is created by
+ accessing the :attr:`.InstanceState.attr`
+ collection.
+
+ """
def __init__(self, state, key):
self.state = state
@@ -473,15 +524,32 @@ class InspectAttr(object):
@property
def loaded_value(self):
+ """The current value of this attribute as loaded from the database.
+
+ If the value has not been loaded, or is otherwise not present
+ in the object's dictionary, returns NO_VALUE.
+
+ """
return self.state.dict.get(self.key, NO_VALUE)
@property
def value(self):
+ """Return the value of this attribute.
+
+ This operation is equivalent to accessing the object's
+ attribute directly or via ``getattr()``, and will fire
+ off any pending loader callables if needed.
+
+ """
return self.state.manager[self.key].__get__(
self.state.obj(), self.state.class_)
@property
def history(self):
+ """Return the current pre-flush change history for
+ this attribute, via the :class:`.History` interface.
+
+ """
return self.state.get_history(self.key,
PASSIVE_NO_INITIALIZE)
diff --git a/lib/sqlalchemy/orm/util.py b/lib/sqlalchemy/orm/util.py
index 517f1acb4..97bf4e7a9 100644
--- a/lib/sqlalchemy/orm/util.py
+++ b/lib/sqlalchemy/orm/util.py
@@ -563,7 +563,40 @@ AliasedInsp = util.namedtuple("AliasedInsp", [
])
class AliasedInsp(_InspectionAttr, AliasedInsp):
+ """Provide an inspection interface for an
+ :class:`.AliasedClass` object.
+
+ The :class:`.AliasedInsp` object is returned
+ given an :class:`.AliasedClass` using the
+ :func:`.inspect` function::
+
+ from sqlalchemy import inspect
+ from sqlalchemy.orm import aliased
+
+ my_alias = aliased(MyMappedClass)
+ insp = inspect(my_alias)
+
+ Attributes on :class:`.AliasedInsp`
+ include:
+
+ * ``entity`` - the :class:`.AliasedClass` represented.
+ * ``mapper`` - the :class:`.Mapper` mapping the underlying class.
+ * ``selectable`` - the :class:`.Alias` construct which ultimately
+ represents an aliased :class:`.Table` or :class:`.Select`
+ construct.
+ * ``name`` - the name of the alias. Also is used as the attribute
+ name when returned in a result tuple from :class:`.Query`.
+ * ``with_polymorphic_mappers`` - collection of :class:`.Mapper` objects
+ indicating all those mappers expressed in the select construct
+ for the :class:`.AliasedClass`.
+ * ``polymorphic_on`` - an alternate column or SQL expression which
+ will be used as the "discriminator" for a polymorphic load.
+
+ """
+
is_aliased_class = True
+ "always returns True"
+
inspection._inspects(AliasedClass)(lambda target: target._aliased_insp)
@@ -864,28 +897,35 @@ def object_mapper(instance):
"""Given an object, return the primary Mapper associated with the object
instance.
- Raises UnmappedInstanceError if no mapping is configured.
+ Raises :class:`sqlalchemy.orm.exc.UnmappedInstanceError`
+ if no mapping is configured.
This function is available via the inspection system as::
inspect(instance).mapper
+ Using the inspection system will raise plain
+ :class:`sqlalchemy.exc.NoInspectionAvailable` if the instance is
+ not part of a mapping.
+
"""
return object_state(instance).mapper
def object_state(instance):
- """Given an object, return the primary Mapper associated with the object
- instance.
+ """Given an object, return the :class:`.InstanceState`
+ associated with the object.
- Raises UnmappedInstanceError if no mapping is configured.
+ Raises :class:`sqlalchemy.orm.exc.UnmappedInstanceError`
+ if no mapping is configured.
- Equivalent functionality is available via the inspection system as::
+ Equivalent functionality is available via the :func:`.inspect`
+ function as::
inspect(instance)
- Using the inspection system will raise plain
- :class:`.InvalidRequestError` if the instance is not part of
- a mapping.
+ Using the inspection system will raise
+ :class:`sqlalchemy.exc.NoInspectionAvailable` if the instance is
+ not part of a mapping.
"""
state = _inspect_mapped_object(instance)
@@ -902,7 +942,8 @@ def class_mapper(class_, configure=True):
on the given class, or :class:`.ArgumentError` if a non-class
object is passed.
- Equivalent functionality is available via the inspection system as::
+ Equivalent functionality is available via the :func:`.inspect`
+ function as::
inspect(some_mapped_class)
diff --git a/lib/sqlalchemy/util/langhelpers.py b/lib/sqlalchemy/util/langhelpers.py
index 8d08da4d8..e9e8e35c2 100644
--- a/lib/sqlalchemy/util/langhelpers.py
+++ b/lib/sqlalchemy/util/langhelpers.py
@@ -15,7 +15,8 @@ import re
import sys
import types
import warnings
-from compat import update_wrapper, set_types, threading, callable, inspect_getfullargspec, py3k_warning
+from compat import update_wrapper, set_types, threading, \
+ callable, inspect_getfullargspec, py3k_warning
from sqlalchemy import exc
def _unique_symbols(used, *bases):
@@ -80,7 +81,7 @@ class PluginLoader(object):
from sqlalchemy import exc
raise exc.ArgumentError(
- "Can't load plugin: %s:%s" %
+ "Can't load plugin: %s:%s" %
(self.group, name))
def register(self, name, modulepath, objname):
@@ -202,7 +203,8 @@ def format_argspec_plus(fn, grouped=True):
self_arg = None
# Py3K
- #apply_pos = inspect.formatargspec(spec[0], spec[1], spec[2], None, spec[4])
+ #apply_pos = inspect.formatargspec(spec[0], spec[1],
+ # spec[2], None, spec[4])
#num_defaults = 0
#if spec[3]:
# num_defaults += len(spec[3])
@@ -218,11 +220,12 @@ def format_argspec_plus(fn, grouped=True):
# end Py2K
if num_defaults:
- defaulted_vals = name_args[0-num_defaults:]
+ defaulted_vals = name_args[0 - num_defaults:]
else:
defaulted_vals = ()
- apply_kw = inspect.formatargspec(name_args, spec[1], spec[2], defaulted_vals,
+ apply_kw = inspect.formatargspec(name_args, spec[1], spec[2],
+ defaulted_vals,
formatvalue=lambda x: '=' + x)
if grouped:
return dict(args=args, self_arg=self_arg,
@@ -281,7 +284,7 @@ def unbound_method_to_callable(func_or_cls):
def generic_repr(obj):
"""Produce a __repr__() based on direct association of the __init__()
specification vs. same-named attributes present.
-
+
"""
def genargs():
try:
@@ -590,10 +593,10 @@ class importlater(object):
from mypackage.somemodule import somesubmod
except evaluted upon attribute access to "somesubmod".
-
+
importlater() currently requires that resolve_all() be
called, typically at the bottom of a package's __init__.py.
- This is so that __import__ still called only at
+ This is so that __import__ still called only at
module import time, and not potentially within
a non-main thread later on.
@@ -623,7 +626,7 @@ class importlater(object):
if self in importlater._unresolved:
raise ImportError(
"importlater.resolve_all() hasn't "
- "been called (this is %s %s)"
+ "been called (this is %s %s)"
% (self._il_path, self._il_addtl))
m = self._initial_import
@@ -638,14 +641,14 @@ class importlater(object):
importlater._unresolved.discard(self)
if self._il_addtl:
self._initial_import = __import__(
- self._il_path, globals(), locals(),
+ self._il_path, globals(), locals(),
[self._il_addtl])
else:
self._initial_import = __import__(self._il_path)
def __getattr__(self, key):
if key == 'module':
- raise ImportError("Could not resolve module %s"
+ raise ImportError("Could not resolve module %s"
% self._full_path)
try:
attr = getattr(self.module, key)
@@ -918,8 +921,8 @@ def warn(msg, stacklevel=3):
If msg is a string, :class:`.exc.SAWarning` is used as
the category.
- .. note::
-
+ .. note::
+
This function is swapped out when the test suite
runs, with a compatible version that uses
warnings.warn_explicit, so that the warnings registry can