Merge lp:~pitti/aptdaemon/pygobject-fixes into lp:aptdaemon

Proposed by Martin Pitt
Status: Merged
Merged at revision: 871
Proposed branch: lp:~pitti/aptdaemon/pygobject-fixes
Merge into: lp:aptdaemon
Diff against target: 68 lines (+13/-26)
2 files modified
aptdaemon/client.py (+11/-26)
tests/test_client.py (+2/-0)
To merge this branch: bzr merge lp:~pitti/aptdaemon/pygobject-fixes
Reviewer Review Type Date Requested Status
Michael Vogt Approve
Martin Pitt (community) Needs Resubmitting
Review via email: mp+134942@code.launchpad.net

Description of the change

This makes aptdaemon work with both PyGObject 3.7.2 as well as older versions (tested 3.4.2). See bug 1080736 for details.

To post a comment you must log in.
Revision history for this message
Michael Vogt (mvo) wrote :

AFAICT this is not exactly what we want. The __call__ override in the metaclass is called everytime that a new AptTransaction() is created and it will return the transaction from the cache if its there already. This will
only work for metaclasses AFAICT.

With the branch this appears to be no longer working, i.e. the following testcase illustrates it:
$ bzr diff
=== modified file 'tests/test_client.py'
--- tests/test_client.py 2012-08-28 10:50:54 +0000
+++ tests/test_client.py 2012-11-22 15:42:12 +0000
@@ -110,6 +110,11 @@
         self.assertEqual(deferred.result.value.code,
                          aptdaemon.enums.ERROR_DEP_RESOLUTION_FAILED)

+ def test_tid(self):
+ tid = "/meep"
+ trans = aptdaemon.client.AptTransaction(tid, None)
+ trans2 = aptdaemon.client.AptTransaction(tid, None)
+ self.assertEqual(trans, trans2)

 if __name__ == "__main__":

This will work in trunk but fail in this branch. The test should probably be added to the branch just to ensure the behavior stays consistent.

review: Needs Fixing
lp:~pitti/aptdaemon/pygobject-fixes updated
867. By Martin Pitt

Add test for TID caching

aptdaemon.client has some metaclass magic to cache transactions with the same
transaction ID. Add a test case that this works properly.

Suggested by Michael Vogt and spotted in
https://code.launchpad.net/~pitti/aptdaemon/pygobject-fixes/+merge/134942

Revision history for this message
Martin Pitt (pitti) wrote :

Indeed, thanks for this! I committed this test case to trunk already, as it's independent from this proposed change:
http://bazaar.launchpad.net/~aptdaemon-developers/aptdaemon/main/revision/867

Revision history for this message
Martin Pitt (pitti) wrote :

Right, so I misunderstood what this is supposed to do. This is supposed to intercept a constructor call, not a method call on an already constructed Transaction object (which is why this first approach doesn't work).

Revision history for this message
Martin Pitt (pitti) wrote :

The new patch overrides __new__() which is the place to do changes in object creation. This satisfies the new test case for verifying the caching as well.

One thing that needs to be investigated with this is this failure:

======================================================================
FAIL: test_replace (tests.test_configfileprompt.ConfigFilePromptTestCase)
Test replacing the current configuration file.
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/martin/upstream/aptdaemon/tests/test_configfileprompt.py", line 114, in test_replace
    "#Just another config file.\n")
AssertionError: 'BliBlaBlub' != '#Just another config file.\n'
    "'BliBlaBlub' != '#Just another config file.\\n'" = '%s != %s' % (safe_repr('BliBlaBlub'), safe_repr('#Just another config file.\n'))
    "'BliBlaBlub' != '#Just another config file.\\n'" = self._formatMessage("'BliBlaBlub' != '#Just another config file.\\n'", "'BliBlaBlub' != '#Just another config file.\\n'")
>> raise self.failureException("'BliBlaBlub' != '#Just another config file.\\n'")

I did not get this without the patch, but this looks totally unrelated to transaction caching, and perhaps might be a race condition?

review: Needs Resubmitting
lp:~pitti/aptdaemon/pygobject-fixes updated
868. By Michael Vogt

tests/test_pk.py: fix long keyid test

869. By Michael Vogt

setup.py: add workaround for nose failure

870. By Michael Vogt

tests/test_pk.py: *cough* typo *cough*

Revision history for this message
Michael Vogt (mvo) wrote :

Thanks for your update.

It appears the caching is now working but a bit too agressive, here is a testcase that fails with the branch:

    def test_tid_not_equal(self):
        trans = aptdaemon.client.AptTransaction("/tid1", None)
        trans2 = aptdaemon.client.AptTransaction("/tid2", None)
        self.assertNotEqual(trans.tid, trans2.tid)

The problem appears to be that the first arg of "__new__" is the class, not the instance, so the cache will
only ever have one entry AFAICT.

Revision history for this message
Martin Pitt (pitti) wrote :

Argh, of course! Thanks for spotting. Fixed now. Now this works fine with python2 + pygi 3.4, python3 + pygi 3.4, and python3 + pygi 3.7.2.

lp:~pitti/aptdaemon/pygobject-fixes updated
871. By Martin Pitt

Drop MemoizedTransaction mixin.

This uses internal implementation details of PyGObject (GObject.GObjectMeta)
which stopped working with PyGObject 3.7.2. Replace it with overriding __new__
which does the caching.

Extend ClientTest.test_tid_caching to ensure that different TIDs actually
generate different transactions.

Revision history for this message
Michael Vogt (mvo) wrote :

Thanks again for the update. This is looking good now

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'aptdaemon/client.py'
2--- aptdaemon/client.py 2012-08-09 18:57:09 +0000
3+++ aptdaemon/client.py 2012-11-26 10:07:24 +0000
4@@ -51,32 +51,7 @@
5 __all__ = ("AptTransaction", "AptClient", "get_transaction", "get_aptdaemon")
6
7
8-class MemoizedTransaction(type):
9-
10- """Metaclass to cache transactions."""
11-
12- cache = weakref.WeakValueDictionary()
13-
14- def __call__(mcs, *args):
15- tid = args[0]
16- try:
17- return mcs.cache[tid]
18- except KeyError:
19- mcs.cache[tid] = value = \
20- super(MemoizedTransaction, mcs).__call__(*args)
21- return value
22-
23-
24-class MemoizedMixInMeta(MemoizedTransaction, GObject.GObjectMeta):
25-
26- """Helper meta class for merging"""
27-
28-# This code is used to make meta classes work with Python 2 and 3 at the same
29-# time, see http://mikewatkins.ca/2008/11/29/python-2-and-3-metaclasses/
30-MemoizedMixIn = MemoizedMixInMeta("MemoizedMixIn", (GObject.GObject,), {})
31-
32-
33-class AptTransaction(MemoizedMixIn):
34+class AptTransaction(GObject.Object):
35
36 """Represents an aptdaemon transaction.
37
38@@ -518,6 +493,16 @@
39 GObject.TYPE_STRING)),
40 }
41
42+ _tid_cache = weakref.WeakValueDictionary()
43+
44+ def __new__(cls, tid, *args):
45+ """Cache transactions with identical tid."""
46+ try:
47+ return AptTransaction._tid_cache[tid]
48+ except KeyError:
49+ AptTransaction._tid_cache[tid] = value = \
50+ GObject.Object.__new__(cls, tid, *args)
51+ return value
52
53 def __init__(self, tid, bus=None):
54 GObject.GObject.__init__(self)
55
56=== modified file 'tests/test_client.py'
57--- tests/test_client.py 2012-11-26 07:16:42 +0000
58+++ tests/test_client.py 2012-11-26 10:07:24 +0000
59@@ -116,7 +116,9 @@
60 tid = "/meep"
61 trans = aptdaemon.client.AptTransaction(tid, None)
62 trans2 = aptdaemon.client.AptTransaction(tid, None)
63+ trans3 = aptdaemon.client.AptTransaction("/meep2", None)
64 self.assertEqual(trans, trans2)
65+ self.assertNotEqual(trans, trans3)
66
67
68 if __name__ == "__main__":

Subscribers

People subscribed via source and target branches

to status/vote changes: