Merge lp:~mvo/python-apt/mvo into lp:~ubuntu-core-dev/python-apt/ubuntu

Proposed by Michael Vogt
Status: Merged
Merged at revision: 493
Proposed branch: lp:~mvo/python-apt/mvo
Merge into: lp:~ubuntu-core-dev/python-apt/ubuntu
Diff against target: 322 lines (+166/-9) (has conflicts)
7 files modified
apt/auth.py (+2/-1)
apt/cache.py (+23/-0)
debian/changelog (+76/-0)
debian/control (+8/-0)
python/generic.h (+4/-0)
python/tag.cc (+2/-2)
tests/test_apt_cache.py (+51/-6)
Text conflict in debian/changelog
Text conflict in debian/control
To merge this branch: bzr merge lp:~mvo/python-apt/mvo
Reviewer Review Type Date Requested Status
Jason Conti (community) Approve
Ubuntu Core Development Team Pending
Review via email: mp+129630@code.launchpad.net

Description of the change

The work from Jason Conti to fix leaking FDs

To post a comment you must log in.
lp:~mvo/python-apt/mvo updated
619. By Michael Vogt

apt/cache.py: add comment

Revision history for this message
Jason Conti (jconti) wrote :

Looks good to me overall. My only concern is that test_cache_delete_leasks_fds() may fail in certain circumstances, and it happened to fail in my test rebuild. Python does eventually get around to deleting the records instance.

I like the other additional tests, and actually had a very similar test to test_cache_close_download_fails() but it didn't occur to me to include it for some reason. It also appears I completely missed the case about leaking fds on cache reopen, thanks for fixing it.

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

On Mon, Oct 15, 2012 at 05:08:22PM -0000, Jason Conti wrote:
> Review: Approve
>
> Looks good to me overall. My only concern is that test_cache_delete_leasks_fds() may fail in certain circumstances, and it happened to fail in my test rebuild. Python does eventually get around to deleting the records instance.

Thanks a lot! Oh, interessting. I had hoped that the gc.collect()
would ensure that its deleted and thereforce closed. Does the test
fails reliable for you? If so, any hints what I can do trying to
reproduce the issue?

> I like the other additional tests, and actually had a very similar test to test_cache_close_download_fails() but it didn't occur to me to include it for some reason. It also appears I completely missed the case about leaking fds on cache reopen, thanks for fixing it.

Great, thanks for the review!

Cheers,
 Michael

lp:~mvo/python-apt/mvo updated
620. By Michael Vogt

tests/test_apt_cache.py: delete test_cache_delete_leasks_fds() as its not reliable

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

On Mon, Oct 15, 2012 at 05:08:22PM -0000, Jason Conti wrote:
> Review: Approve
>
> Looks good to me overall. My only concern is that test_cache_delete_leasks_fds() may fail in certain circumstances, and it happened to fail in my test rebuild. Python does eventually get around to deleting the records instance.
[..]

Nevermind, its also failing for me now, not sure why I did not see
that before. I deleted the test again.

Cheers,
 Michael

lp:~mvo/python-apt/mvo updated
621. By Michael Vogt

build fixes for python3.3

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'apt/auth.py'
--- apt/auth.py 2012-10-12 09:06:34 +0000
+++ apt/auth.py 2012-10-23 10:39:23 +0000
@@ -78,7 +78,8 @@
78 stderr=subprocess.PIPE)78 stderr=subprocess.PIPE)
7979
80 content = kwargs.get("stdin", None)80 content = kwargs.get("stdin", None)
81 if isinstance(content, unicode):81 # py2 needs this encoded, py3.3 will crash if it is
82 if isinstance(content, unicode) and sys.version_info[:2] < (3, 3):
82 content = content.encode("utf-8")83 content = content.encode("utf-8")
8384
84 output, stderr = proc.communicate(content)85 output, stderr = proc.communicate(content)
8586
=== modified file 'apt/cache.py'
--- apt/cache.py 2012-04-17 19:17:10 +0000
+++ apt/cache.py 2012-10-23 10:39:23 +0000
@@ -42,6 +42,9 @@
42class LockFailedException(IOError):42class LockFailedException(IOError):
43 """Exception that is thrown when locking fails."""43 """Exception that is thrown when locking fails."""
4444
45class CacheClosedException(Exception):
46 """Exception that is thrown when the cache is used after close()."""
47
4548
46class Cache(object):49class Cache(object):
47 """Dictionary-like package cache.50 """Dictionary-like package cache.
@@ -139,6 +142,8 @@
139 """142 """
140 if progress is None:143 if progress is None:
141 progress = apt.progress.base.OpProgress()144 progress = apt.progress.base.OpProgress()
145 # close old cache on (re)open
146 self.close()
142 self.op_progress = progress147 self.op_progress = progress
143 self._run_callbacks("cache_pre_open")148 self._run_callbacks("cache_pre_open")
144149
@@ -172,6 +177,20 @@
172 progress.done()177 progress.done()
173 self._run_callbacks("cache_post_open")178 self._run_callbacks("cache_post_open")
174179
180 def close(self):
181 """ Close the package cache """
182 # explicitely free the FDs that _records has open
183 del self._records
184 self._records = None
185
186 def __enter__(self):
187 """ Enter the with statement """
188 return self
189
190 def __exit__(self, exc_type, exc_value, traceback):
191 """ Exit the with statement """
192 self.close()
193
175 def __getitem__(self, key):194 def __getitem__(self, key):
176 """ look like a dictionary (get key) """195 """ look like a dictionary (get key) """
177 try:196 try:
@@ -238,6 +257,8 @@
238 @property257 @property
239 def required_download(self):258 def required_download(self):
240 """Get the size of the packages that are required to download."""259 """Get the size of the packages that are required to download."""
260 if self._records is None:
261 raise CacheClosedException("Cache object used after close() called")
241 pm = apt_pkg.PackageManager(self._depcache)262 pm = apt_pkg.PackageManager(self._depcache)
242 fetcher = apt_pkg.Acquire()263 fetcher = apt_pkg.Acquire()
243 pm.get_archives(fetcher, self._list, self._records)264 pm.get_archives(fetcher, self._list, self._records)
@@ -288,6 +309,8 @@
288309
289 def _fetch_archives(self, fetcher, pm):310 def _fetch_archives(self, fetcher, pm):
290 """ fetch the needed archives """311 """ fetch the needed archives """
312 if self._records is None:
313 raise CacheClosedException("Cache object used after close() called")
291314
292 # get lock315 # get lock
293 lockfile = apt_pkg.config.find_dir("Dir::Cache::Archives") + "lock"316 lockfile = apt_pkg.config.find_dir("Dir::Cache::Archives") + "lock"
294317
=== modified file 'debian/changelog'
--- debian/changelog 2012-10-12 09:11:14 +0000
+++ debian/changelog 2012-10-23 10:39:23 +0000
@@ -1,3 +1,4 @@
1<<<<<<< TREE
1python-apt (0.8.8ubuntu1) UNRELEASED; urgency=low2python-apt (0.8.8ubuntu1) UNRELEASED; urgency=low
23
3 NOTE: THIS is intended for R, use lp:~ubuntu-core-dev/python-apt/quantal4 NOTE: THIS is intended for R, use lp:~ubuntu-core-dev/python-apt/quantal
@@ -107,6 +108,81 @@
107108
108 -- Michael Vogt <michael.vogt@ubuntu.com> Fri, 29 Jun 2012 16:05:52 +0200109 -- Michael Vogt <michael.vogt@ubuntu.com> Fri, 29 Jun 2012 16:05:52 +0200
109110
111=======
112python-apt (0.8.8.1) UNRELEASED; urgency=low
113
114 [ Michael Vogt ]
115 * python/tag.cc:
116 - make TagSecString_FromStringAndSize, TagSecString_FromString
117 static, thanks to jcristau
118 * python/cache.cc:
119 - add "Codename" to PackageFile object
120 * add dep8 style autopkgtest support
121 * build fixes for python3.3
122
123 [ Jason Conti ]
124 * lp:~jconti/python-apt/closeable-cache:
125 - add apt.Cache.close() method
126
127 -- Michael Vogt <michael.vogt@ubuntu.com> Mon, 15 Oct 2012 10:03:21 +0200
128
129python-apt (0.8.8) unstable; urgency=low
130
131 [ Program translation updates ]
132 * po/pl.po: Polish (Michał Kułach) (closes: #684308)
133 * po/da.po: Danish (Joe Hansen) (closes: #689827)
134
135 [ Michael Vogt ]
136 * merged lp:~sampo555/python-apt/fix_1042916 reuse existing but
137 disabled sources.list entries instead of duplicating them.
138 Thanks to "sampo555", LP: #1042916
139 * lp:~mvo/python-apt/fix-debfile-crash:
140 - fix crash on missing candidates in the multiarch check
141 * lp:~mvo/python-apt/recv-key-lp1016643:
142 - Only support long (v4) keyids when downloading keys and
143 check the keys fingerprint before importing. This avoids
144 man-in-the-middle attacks (LP: #1016643)
145 * consolidate tests/test_lp1030278.py into the new
146 tests/test_size_to_str.py
147 * apt/auth.py:
148 - support importing long keyids with leading 0x and mixed case
149 * debian/control:
150 - build-depend on python-unittest2 to get "with TestCase.assertRaises"
151 support in python2.6
152
153 [ Barry Warsaw ]
154 * python/string.cc, tests/test_lp1030278.py: Fix StrSizeToStr() so that
155 1) it first checks for PyLong-ness so that in Python 3 on i386, it
156 will be able to convert larger numbers (via doubles rather than ints);
157 2) before doing the conversions through the apt API, check to see if a
158 Python exception occurred, e.g. OverflowError, and return an error
159 condition in that case instead of masking it. (LP: #1030278)
160
161 [ James Hunt ]
162 * python/cache.cc: PkgCacheGetIsMultiArch(): Return calculated
163 value rather than a random one.
164 * lp:~jamesodhunt/python-apt/test-for-size_to_str:
165 - add test for size_to_str() to help with finding LP: #1030278
166
167 -- Michael Vogt <mvo@debian.org> Fri, 12 Oct 2012 10:47:11 +0200
168
169python-apt (0.8.7) unstable; urgency=low
170
171 [ Translation updates ]
172 * po/es.po: Spanish translation updated by Omar Campagne (closes: #679285)
173 * po/ja.po: Japanese translation updated by Kenshi Muto (closes: #679652)
174
175 [ Jakub Wilk ]
176 * Fix typos: the the -> the (closes: #679432)
177
178 [ Julian Andres Klode ]
179 * apt/auth.py:
180 - Do not merge stdout and stderr (Closes: #678706)
181 - Forward stderr from apt-key to our stderr if non-empty
182
183 -- Julian Andres Klode <jak@debian.org> Mon, 30 Jul 2012 13:29:17 +0200
184
185>>>>>>> MERGE-SOURCE
110python-apt (0.8.6) unstable; urgency=low186python-apt (0.8.6) unstable; urgency=low
111187
112 [ Michael Vogt ]188 [ Michael Vogt ]
113189
=== modified file 'debian/control'
--- debian/control 2012-10-12 09:06:34 +0000
+++ debian/control 2012-10-23 10:39:23 +0000
@@ -18,11 +18,19 @@
18 python3-all-dbg (>= 3.1.2-6~),18 python3-all-dbg (>= 3.1.2-6~),
19 python-distutils-extra (>= 2.0),19 python-distutils-extra (>= 2.0),
20 python-sphinx (>= 0.5),20 python-sphinx (>= 0.5),
21<<<<<<< TREE
21 python-debian22 python-debian
22Vcs-Bzr: http://code.launchpad.net/~ubuntu-core-dev/python-apt/ubuntu23Vcs-Bzr: http://code.launchpad.net/~ubuntu-core-dev/python-apt/ubuntu
23XS-Debian-Vcs-Bzr: http://bzr.debian.org/apt/python-apt/debian-sid24XS-Debian-Vcs-Bzr: http://bzr.debian.org/apt/python-apt/debian-sid
24XS-Debian-Vcs-Browser: http://bzr.debian.org/loggerhead/apt/python-apt/debian-sid/changes25XS-Debian-Vcs-Browser: http://bzr.debian.org/loggerhead/apt/python-apt/debian-sid/changes
25XS-Testsuite: autopkgtest26XS-Testsuite: autopkgtest
27=======
28 python-debian,
29 python-unittest2
30Vcs-Bzr: http://bzr.debian.org/apt/python-apt/debian-sid
31Vcs-Browser: http://bzr.debian.org/loggerhead/apt/python-apt/debian-sid/changes
32XS-Testsuite: autopkgtest
33>>>>>>> MERGE-SOURCE
2634
27Package: python-apt35Package: python-apt
28Architecture: any36Architecture: any
2937
=== modified file 'python/generic.h'
--- python/generic.h 2011-08-01 07:29:25 +0000
+++ python/generic.h 2012-10-23 10:39:23 +0000
@@ -80,10 +80,14 @@
8080
81static inline const char *PyUnicode_AsString(PyObject *op) {81static inline const char *PyUnicode_AsString(PyObject *op) {
82 // Convert to bytes object, using the default encoding.82 // Convert to bytes object, using the default encoding.
83#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 3
84 return PyUnicode_AsUTF8(op);
85#else
83 // Use Python-internal API, there is no other way to do this86 // Use Python-internal API, there is no other way to do this
84 // without a memory leak.87 // without a memory leak.
85 PyObject *bytes = _PyUnicode_AsDefaultEncodedString(op, 0);88 PyObject *bytes = _PyUnicode_AsDefaultEncodedString(op, 0);
86 return bytes ? PyBytes_AS_STRING(bytes) : 0;89 return bytes ? PyBytes_AS_STRING(bytes) : 0;
90#endif
87}91}
8892
89// Convert any type of string based object to a const char.93// Convert any type of string based object to a const char.
9094
=== modified file 'python/tag.cc'
--- python/tag.cc 2012-06-17 21:11:16 +0000
+++ python/tag.cc 2012-10-23 10:39:23 +0000
@@ -74,7 +74,7 @@
74 PyString_FromStringAndSize((v), (len))74 PyString_FromStringAndSize((v), (len))
75#define TagSecString_FromString(self, v) PyString_FromString(v)75#define TagSecString_FromString(self, v) PyString_FromString(v)
76#else76#else
77PyObject *TagSecString_FromStringAndSize(PyObject *self, const char *v,77static PyObject *TagSecString_FromStringAndSize(PyObject *self, const char *v,
78 Py_ssize_t len) {78 Py_ssize_t len) {
79 TagSecData *Self = (TagSecData *)self;79 TagSecData *Self = (TagSecData *)self;
80 if (Self->Bytes)80 if (Self->Bytes)
@@ -85,7 +85,7 @@
85 return PyUnicode_FromStringAndSize(v, len);85 return PyUnicode_FromStringAndSize(v, len);
86}86}
8787
88PyObject *TagSecString_FromString(PyObject *self, const char *v) {88static PyObject *TagSecString_FromString(PyObject *self, const char *v) {
89 TagSecData *Self = (TagSecData *)self;89 TagSecData *Self = (TagSecData *)self;
90 if (Self->Bytes)90 if (Self->Bytes)
91 return PyBytes_FromString(v);91 return PyBytes_FromString(v);
9292
=== modified file 'tests/test_apt_cache.py'
--- tests/test_apt_cache.py 2011-12-09 08:16:08 +0000
+++ tests/test_apt_cache.py 2012-10-23 10:39:23 +0000
@@ -7,19 +7,27 @@
7# are permitted in any medium without royalty provided the copyright7# are permitted in any medium without royalty provided the copyright
8# notice and this notice are preserved.8# notice and this notice are preserved.
9"""Unit tests for verifying the correctness of check_dep, etc in apt_pkg."""9"""Unit tests for verifying the correctness of check_dep, etc in apt_pkg."""
10
11import glob
12import logging
10import os13import os
14import shutil
15import sys
11import tempfile16import tempfile
12import unittest17import unittest
1318
19if sys.version_info[0] == 2 and sys.version_info[1] == 6:
20 from unittest2 import TestCase
21else:
22 from unittest import TestCase
23
24
14from test_all import get_library_dir25from test_all import get_library_dir
15import sys
16sys.path.insert(0, get_library_dir())26sys.path.insert(0, get_library_dir())
1727
18import apt28import apt
19import apt_pkg29import apt_pkg
20import shutil30
21import glob
22import logging
2331
24def if_sources_list_is_readable(f):32def if_sources_list_is_readable(f):
25 def wrapper(*args, **kwargs):33 def wrapper(*args, **kwargs):
@@ -29,7 +37,17 @@
29 logging.warn("skipping '%s' because sources.list is not readable" % f)37 logging.warn("skipping '%s' because sources.list is not readable" % f)
30 return wrapper38 return wrapper
3139
32class TestAptCache(unittest.TestCase):40
41def get_open_file_descriptors():
42 try:
43 fds = os.listdir("/proc/self/fd")
44 except OSError:
45 logging.warn("failed to list /proc/self/fd")
46 return set([])
47 return set(map(int, fds))
48
49
50class TestAptCache(TestCase):
33 """ test the apt cache """51 """ test the apt cache """
3452
35 def setUp(self):53 def setUp(self):
@@ -68,7 +86,34 @@
68 self.assertEqual(r['Package'], pkg.shortname)86 self.assertEqual(r['Package'], pkg.shortname)
69 self.assertTrue('Version' in r)87 self.assertTrue('Version' in r)
70 self.assertTrue(len(r['Description']) > 0)88 self.assertTrue(len(r['Description']) > 0)
71 self.assertTrue(str(r).startswith('Package: %s\n' % pkg.shortname))89 self.assertTrue(
90 str(r).startswith('Package: %s\n' % pkg.shortname))
91
92 @if_sources_list_is_readable
93 def test_cache_close_leak_fd(self):
94 fds_before_open = get_open_file_descriptors()
95 cache = apt.Cache()
96 opened_fd = get_open_file_descriptors().difference(fds_before_open)
97 cache.close()
98 fds_after_close = get_open_file_descriptors()
99 unclosed_fd = opened_fd.intersection(fds_after_close)
100 self.assertEqual(fds_before_open, fds_after_close)
101 self.assertEqual(unclosed_fd, set())
102
103 def test_cache_open_twice_leaks_fds(self):
104 cache = apt.Cache()
105 fds_before_open = get_open_file_descriptors()
106 cache.open()
107 fds_after_open_twice = get_open_file_descriptors()
108 self.assertEqual(fds_before_open, fds_after_open_twice)
109
110 @if_sources_list_is_readable
111 def test_cache_close_download_fails(self):
112 cache = apt.Cache()
113 self.assertEqual(cache.required_download, 0)
114 cache.close()
115 with self.assertRaises(apt.cache.CacheClosedException):
116 cache.required_download
72117
73 def test_get_provided_packages(self):118 def test_get_provided_packages(self):
74 apt.apt_pkg.config.set("Apt::architecture", "i386")119 apt.apt_pkg.config.set("Apt::architecture", "i386")

Subscribers

People subscribed via source and target branches

to all changes: