<p dir="auto"><strong>Migrated issue, originally created by Pablo Marti (<a href="https://github.com/pmarti">@pmarti</a>)</strong></p>
<p dir="auto">Hi there,</p>
<p dir="auto">I have a User model class with a profile attribute marked as mutable</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="class User(db.Model):
    ...
    profile = db.Column(MutableDict.as_mutable(JSONB()))"><pre class="notranslate"><code class="notranslate">class User(db.Model):
    ...
    profile = db.Column(MutableDict.as_mutable(JSONB()))
</code></pre></div>
<p dir="auto">This works perfectly under python2.7, but now when I issue a query using pypy 2.6 I get this:</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="Traceback (most recent call last):
  File &quot;/Users/pablo/.virtualenvs/drop/src/flask/flask/app.py&quot;, line 1537, in full_dispatch_request
    rv = self.dispatch_request()
  File &quot;/Users/pablo/.virtualenvs/drop/src/flask/flask/app.py&quot;, line 1523, in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
  File &quot;/Users/pablo/Development/drop-server/drop/api/decorators.py&quot;, line 73, in decorated_function
    .get(user_id)
  File &quot;/Users/pablo/.virtualenvs/drop/site-packages/sqlalchemy/orm/query.py&quot;, line 818, in get
    return self._get_impl(ident, loading.load_on_ident)
  File &quot;/Users/pablo/.virtualenvs/drop/site-packages/sqlalchemy/orm/query.py&quot;, line 851, in _get_impl
    return fallback_fn(self, key)
  File &quot;/Users/pablo/.virtualenvs/drop/site-packages/sqlalchemy/orm/loading.py&quot;, line 217, in load_on_ident
    return q.one()
  File &quot;/Users/pablo/.virtualenvs/drop/site-packages/sqlalchemy/orm/query.py&quot;, line 2472, in one
    ret = list(self)
  File &quot;/Users/pablo/.virtualenvs/drop/site-packages/sqlalchemy/orm/loading.py&quot;, line 84, in instances
    util.raise_from_cause(err)
  File &quot;/Users/pablo/.virtualenvs/drop/site-packages/sqlalchemy/util/compat.py&quot;, line 199, in raise_from_cause
    reraise(type(exception), exception, tb=exc_tb)
  File &quot;/Users/pablo/.virtualenvs/drop/site-packages/sqlalchemy/orm/loading.py&quot;, line 69, in instances
    rows = [proc(row) for row in fetch]
  File &quot;/Users/pablo/.virtualenvs/drop/site-packages/sqlalchemy/orm/loading.py&quot;, line 430, in _instance
    state.manager.dispatch.load(state, context)
  File &quot;/Users/pablo/.virtualenvs/drop/site-packages/sqlalchemy/event/attr.py&quot;, line 258, in __call__
    fn(*args, **kw)
  File &quot;/Users/pablo/.virtualenvs/drop/site-packages/sqlalchemy/ext/mutable.py&quot;, line 428, in load
    val = cls.coerce(key, val)
  File &quot;/Users/pablo/.virtualenvs/drop/site-packages/sqlalchemy/ext/mutable.py&quot;, line 638, in coerce
    return Mutable.coerce(key, value)
  File &quot;/Users/pablo/.virtualenvs/drop/site-packages/sqlalchemy/ext/mutable.py&quot;, line 403, in coerce
    raise ValueError(msg % (key, type(value)))
ValueError: Attribute 'profile' does not accept objects of type &lt;type 'list'&gt;"><pre class="notranslate"><code class="notranslate">Traceback (most recent call last):
  File "/Users/pablo/.virtualenvs/drop/src/flask/flask/app.py", line 1537, in full_dispatch_request
    rv = self.dispatch_request()
  File "/Users/pablo/.virtualenvs/drop/src/flask/flask/app.py", line 1523, in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
  File "/Users/pablo/Development/drop-server/drop/api/decorators.py", line 73, in decorated_function
    .get(user_id)
  File "/Users/pablo/.virtualenvs/drop/site-packages/sqlalchemy/orm/query.py", line 818, in get
    return self._get_impl(ident, loading.load_on_ident)
  File "/Users/pablo/.virtualenvs/drop/site-packages/sqlalchemy/orm/query.py", line 851, in _get_impl
    return fallback_fn(self, key)
  File "/Users/pablo/.virtualenvs/drop/site-packages/sqlalchemy/orm/loading.py", line 217, in load_on_ident
    return q.one()
  File "/Users/pablo/.virtualenvs/drop/site-packages/sqlalchemy/orm/query.py", line 2472, in one
    ret = list(self)
  File "/Users/pablo/.virtualenvs/drop/site-packages/sqlalchemy/orm/loading.py", line 84, in instances
    util.raise_from_cause(err)
  File "/Users/pablo/.virtualenvs/drop/site-packages/sqlalchemy/util/compat.py", line 199, in raise_from_cause
    reraise(type(exception), exception, tb=exc_tb)
  File "/Users/pablo/.virtualenvs/drop/site-packages/sqlalchemy/orm/loading.py", line 69, in instances
    rows = [proc(row) for row in fetch]
  File "/Users/pablo/.virtualenvs/drop/site-packages/sqlalchemy/orm/loading.py", line 430, in _instance
    state.manager.dispatch.load(state, context)
  File "/Users/pablo/.virtualenvs/drop/site-packages/sqlalchemy/event/attr.py", line 258, in __call__
    fn(*args, **kw)
  File "/Users/pablo/.virtualenvs/drop/site-packages/sqlalchemy/ext/mutable.py", line 428, in load
    val = cls.coerce(key, val)
  File "/Users/pablo/.virtualenvs/drop/site-packages/sqlalchemy/ext/mutable.py", line 638, in coerce
    return Mutable.coerce(key, value)
  File "/Users/pablo/.virtualenvs/drop/site-packages/sqlalchemy/ext/mutable.py", line 403, in coerce
    raise ValueError(msg % (key, type(value)))
ValueError: Attribute 'profile' does not accept objects of type &lt;type 'list'&gt;
</code></pre></div>
<p dir="auto">Dependencies:</p>
<p dir="auto">SQLAlchemy==1.0.4<br>
psycopg2cffi==2.7.0</p>
<p dir="auto"><strong>Migrated issue, originally created by Raphaël Slinckx</strong></p>
<p dir="auto">Here is a test case yielding different results between sqlalchemy 0.9.9 and 1.0+</p>
<div class="highlight highlight-source-python notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="import json

from sqlalchemy import create_engine, Column
from sqlalchemy.types import TypeDecorator, UnicodeText, Integer
from sqlalchemy.ext.mutable import MutableDict
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import Session

class JSONDictType(TypeDecorator):
	impl = UnicodeText

	def process_bind_param(self, value, dialect):
		if value is not None:
			value = unicode(json.dumps(value))
		return value

	def process_result_value(self, value, dialect):
		if value is not None:
			value = json.loads(value)
		return value

def JSON():
	return MutableDict.as_mutable(JSONDictType)

Base = declarative_base()

class MyDataClass(Base):
	__tablename__ = 'my_data'

	id   = Column(Integer, primary_key=True)
	data = Column(JSON(), default={}, nullable=False)

session = Session(create_engine(&quot;sqlite:///:memory:&quot;, echo=True))
Base.metadata.create_all(session.bind)

m1 = MyDataClass()
session.add(m1)
session.flush() # Note: without this, the m1.data is None on sqlalchemy 1.0
m1.data[&quot;test&quot;] = 1
session.commit()

m2 = MyDataClass()
session.add(m2)
session.commit()

assert m1.data == {'test': 1}
assert m2.data == {}
# With sqlalchemy 1.0 this results in
# m1.data == {}
# m2.data == {'test': 1}"><pre class="notranslate"><span class="pl-k">import</span> <span class="pl-s1">json</span>

<span class="pl-k">from</span> <span class="pl-s1">sqlalchemy</span> <span class="pl-k">import</span> <span class="pl-s1">create_engine</span>, <span class="pl-v">Column</span>
<span class="pl-k">from</span> <span class="pl-s1">sqlalchemy</span>.<span class="pl-s1">types</span> <span class="pl-k">import</span> <span class="pl-v">TypeDecorator</span>, <span class="pl-v">UnicodeText</span>, <span class="pl-v">Integer</span>
<span class="pl-k">from</span> <span class="pl-s1">sqlalchemy</span>.<span class="pl-s1">ext</span>.<span class="pl-s1">mutable</span> <span class="pl-k">import</span> <span class="pl-v">MutableDict</span>
<span class="pl-k">from</span> <span class="pl-s1">sqlalchemy</span>.<span class="pl-s1">ext</span>.<span class="pl-s1">declarative</span> <span class="pl-k">import</span> <span class="pl-s1">declarative_base</span>
<span class="pl-k">from</span> <span class="pl-s1">sqlalchemy</span>.<span class="pl-s1">orm</span> <span class="pl-k">import</span> <span class="pl-v">Session</span>

<span class="pl-k">class</span> <span class="pl-v">JSONDictType</span>(<span class="pl-v">TypeDecorator</span>):
	<span class="pl-s1">impl</span> <span class="pl-c1">=</span> <span class="pl-v">UnicodeText</span>

	<span class="pl-k">def</span> <span class="pl-en">process_bind_param</span>(<span class="pl-s1">self</span>, <span class="pl-s1">value</span>, <span class="pl-s1">dialect</span>):
		<span class="pl-k">if</span> <span class="pl-s1">value</span> <span class="pl-c1">is</span> <span class="pl-c1">not</span> <span class="pl-c1">None</span>:
			<span class="pl-s1">value</span> <span class="pl-c1">=</span> <span class="pl-en">unicode</span>(<span class="pl-s1">json</span>.<span class="pl-en">dumps</span>(<span class="pl-s1">value</span>))
		<span class="pl-k">return</span> <span class="pl-s1">value</span>

	<span class="pl-k">def</span> <span class="pl-en">process_result_value</span>(<span class="pl-s1">self</span>, <span class="pl-s1">value</span>, <span class="pl-s1">dialect</span>):
		<span class="pl-k">if</span> <span class="pl-s1">value</span> <span class="pl-c1">is</span> <span class="pl-c1">not</span> <span class="pl-c1">None</span>:
			<span class="pl-s1">value</span> <span class="pl-c1">=</span> <span class="pl-s1">json</span>.<span class="pl-en">loads</span>(<span class="pl-s1">value</span>)
		<span class="pl-k">return</span> <span class="pl-s1">value</span>

<span class="pl-k">def</span> <span class="pl-v">JSON</span>():
	<span class="pl-k">return</span> <span class="pl-v">MutableDict</span>.<span class="pl-en">as_mutable</span>(<span class="pl-v">JSONDictType</span>)

<span class="pl-v">Base</span> <span class="pl-c1">=</span> <span class="pl-en">declarative_base</span>()

<span class="pl-k">class</span> <span class="pl-v">MyDataClass</span>(<span class="pl-v">Base</span>):
	<span class="pl-s1">__tablename__</span> <span class="pl-c1">=</span> <span class="pl-s">'my_data'</span>

	<span class="pl-s1">id</span>   <span class="pl-c1">=</span> <span class="pl-v">Column</span>(<span class="pl-v">Integer</span>, <span class="pl-s1">primary_key</span><span class="pl-c1">=</span><span class="pl-c1">True</span>)
	<span class="pl-s1">data</span> <span class="pl-c1">=</span> <span class="pl-v">Column</span>(<span class="pl-v">JSON</span>(), <span class="pl-s1">default</span><span class="pl-c1">=</span>{}, <span class="pl-s1">nullable</span><span class="pl-c1">=</span><span class="pl-c1">False</span>)

<span class="pl-s1">session</span> <span class="pl-c1">=</span> <span class="pl-v">Session</span>(<span class="pl-en">create_engine</span>(<span class="pl-s">"sqlite:///:memory:"</span>, <span class="pl-s1">echo</span><span class="pl-c1">=</span><span class="pl-c1">True</span>))
<span class="pl-v">Base</span>.<span class="pl-s1">metadata</span>.<span class="pl-en">create_all</span>(<span class="pl-s1">session</span>.<span class="pl-s1">bind</span>)

<span class="pl-s1">m1</span> <span class="pl-c1">=</span> <span class="pl-v">MyDataClass</span>()
<span class="pl-s1">session</span>.<span class="pl-en">add</span>(<span class="pl-s1">m1</span>)
<span class="pl-s1">session</span>.<span class="pl-en">flush</span>() <span class="pl-c"># Note: without this, the m1.data is None on sqlalchemy 1.0</span>
<span class="pl-s1">m1</span>.<span class="pl-s1">data</span>[<span class="pl-s">"test"</span>] <span class="pl-c1">=</span> <span class="pl-c1">1</span>
<span class="pl-s1">session</span>.<span class="pl-en">commit</span>()

<span class="pl-s1">m2</span> <span class="pl-c1">=</span> <span class="pl-v">MyDataClass</span>()
<span class="pl-s1">session</span>.<span class="pl-en">add</span>(<span class="pl-s1">m2</span>)
<span class="pl-s1">session</span>.<span class="pl-en">commit</span>()

<span class="pl-k">assert</span> <span class="pl-s1">m1</span>.<span class="pl-s1">data</span> <span class="pl-c1">==</span> {<span class="pl-s">'test'</span>: <span class="pl-c1">1</span>}
<span class="pl-k">assert</span> <span class="pl-s1">m2</span>.<span class="pl-s1">data</span> <span class="pl-c1">==</span> {}
<span class="pl-c"># With sqlalchemy 1.0 this results in</span>
<span class="pl-c"># m1.data == {}</span>
<span class="pl-c"># m2.data == {'test': 1}</span></pre></div>
<p dir="auto">As you can see the default={} seemed to work previously (also the m1.data['test'] = 1 worked without the session.flush() just above)</p>
<p dir="auto">With the latest version, the dict provided as default seems to be used and re-used resulting in a process-wide globally shared default dict which can lead to catastrophic leaks in a webapp for example. Also the object needs to be flushed before m1.data is populated.</p>
<p dir="auto">Maybe this usage was not expected or even discouraged, but I didn't find anything in the docs about this. The closest thing I identified is the change discussed here: <a href="http://docs.sqlalchemy.org/en/latest/changelog/migration_10.html#changes-to-attribute-events-and-other-operations-regarding-attributes-that-have-no-pre-existing-value" rel="nofollow">http://docs.sqlalchemy.org/en/latest/changelog/migration_10.html#changes-to-attribute-events-and-other-operations-regarding-attributes-that-have-no-pre-existing-value</a> and possibly the new default handling.</p>
<p dir="auto">What is the expected behavior as of 1.0 ? And how do I get the old behavior using the latest version ?</p>