OK so I'll just walk through how I do this:
1. first I run it in py3K where the "random" hash behavior is, until
python 3.6, in effect.
2. I can see the attributes are there or not on each run.
3. next I want to whack the monkeypatched thing, because that's a lot of
code that I don't want to be involved in the issue. Fortunately, just
taking it out, behavior still reproduces. This means it's not part of
the issue.
4. Next, I see the attributes aren't there on the query side, but that
skips ahead, how do we know they were persisted? Turn on echo=True
(the whole ORM / Core tutorials illustrate an echo=True kind of workflow
which is the best way to understand things)
5. echo=True reveals the problem way at the beginning. Good run:
INSERT INTO tags ("key", value) VALUES (?, ?)
2016-09-10 10:18:22,503 INFO sqlalchemy.engine.base.Engine ('foo', 'bar')
2016-09-10 10:18:22,504 INFO sqlalchemy.engine.base.Engine INSERT INTO
tags ("key", value) VALUES (?, ?)
2016-09-10 10:18:22,504 INFO sqlalchemy.engine.base.Engine ('bang', 'baz')
bad run:
INSERT INTO tags ("key", value) VALUES (?, ?)
2016-09-10 10:18:25,567 INFO sqlalchemy.engine.base.Engine ('bang', '')
2016-09-10 10:18:25,568 INFO sqlalchemy.engine.base.Engine INSERT INTO
tags ("key", value) VALUES (?, ?)
2016-09-10 10:18:25,568 INFO sqlalchemy.engine.base.Engine ('foo', '')
(note I haven't begun to look at the models here at all. Way too time
consuming )
6. having a Tag with no "value" is definitely wrong, let's ensure those
are present so we can get a stack trace where they are not. First I
just did a naive "assert value" in the constructor, but OK this is
association proxy trying to set "value" after the fact, OK
7. it looks like getting the "value" there is dependent on the
ElementsTags.tag_value proxy working. Also we observe that the two
objects either both work, or both fail. At this point it's obvious
that the bug has to do with the initialization of the ElementsTags
class, the dictionary ordering issue here probably has to do with the
two association proxy elements getting set up differently.
8. Where do we get a Tag() from? Weird, there is no call to Tag()
anywhere at all. Is this relying on some behavior of association proxy?
The only assocs that could do this are the tag_key and
tag_value...but....which one sets up Tag()? I have no idea how the
association proxy handles this.
9. after testing a few things (is it creating two Tag() objects for each
one? it's not. changing order of attributes affects it? not really)
it looks like I still don't know why value is set or not.
10. Well let's at least confirm this is where the issue originates:
tags = association_proxy("elements_tags", "tag_value",
creator=lambda k, v:
ElementsTags(tag=Tag(key=k, value=v)))
that is, don't try to challenge the association proxy to work in
multiple levels of nesting, just keep it simple. bug is gone. OK, so
association proxy definitely tripping up on setting up via two
association proxies inside the constructor of another (not too
surprising). We know it isn't creating two Tag objects. So, assoc
proxy just doesn't want to set "value" sometimes.
11. let's revisit what we wanted to do in #6 but more appropriately:
class ElementsTags(base):
def __init__(self, **kw):
super(ElementsTags, self).__init__(**kw)
assert self.tag.value
# ...
Now I can get the real stack trace on alternate runs:
Traceback (most recent call last):
File "test.py", line 58, in <module>
e.tags[u"foo"] = u"bar"
File
"/home/classic/dev/sqlalchemy/lib/sqlalchemy/ext/associationproxy.py",
line 742, in __setitem__
self.col[key] = self._create(key, value)
File
"/home/classic/dev/sqlalchemy/lib/sqlalchemy/ext/associationproxy.py",
line 727, in _create
return self.creator(key, value)
File "test.py", line 31, in <lambda>
creator=lambda k, v: ElementsTags(tag_key=k, tag_value=v))
File "<string>", line 4, in __init__
File "/home/classic/dev/sqlalchemy/lib/sqlalchemy/orm/state.py", line
406, in _initialize_instance
manager.dispatch.init_failure(self, args, kwargs)
File
"/home/classic/dev/sqlalchemy/lib/sqlalchemy/util/langhelpers.py", line
60, in __exit__
compat.reraise(exc_type, exc_value, exc_tb)
File "/home/classic/dev/sqlalchemy/lib/sqlalchemy/util/compat.py",
line 186, in reraise
raise value
File "/home/classic/dev/sqlalchemy/lib/sqlalchemy/orm/state.py", line
403, in _initialize_instance
return manager.original_init(*mixed[1:], **kwargs)
File "test.py", line 53, in __init__
assert self.tag.value
So somewhere in assoc proxy, something is happening. I don't know
where the bug is yet, but I have to go. Maybe you can keep pdbing into
association proxy and see where this might be happening.
On 09/10/2016 03:04 AM, Dominik George wrote:
Am Samstag, 10. September 2016 09:02:32 UTC+2 schrieb Dominik George:
Hi Mike,
here it is. See attached mwe.py.
Which is here ;).
-nik
--
You received this message because you are subscribed to the Google
Groups "sqlalchemy" group.
To unsubscribe from this group and stop receiving emails from it, send
an email to [email protected]
<mailto:[email protected]>.
To post to this group, send email to [email protected]
<mailto:[email protected]>.
Visit this group at https://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/d/optout.
--
You received this message because you are subscribed to the Google Groups
"sqlalchemy" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To post to this group, send email to [email protected].
Visit this group at https://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/d/optout.