Merge lp:~xnox/ubuntu-dev-tools/py3k into lp:~ubuntu-dev/ubuntu-dev-tools/trunk

Proposed by Dimitri John Ledkov
Status: Merged
Merged at revision: 1422
Proposed branch: lp:~xnox/ubuntu-dev-tools/py3k
Merge into: lp:~ubuntu-dev/ubuntu-dev-tools/trunk
Diff against target: 1965 lines (+467/-335)
25 files modified
debian/changelog (+3/-0)
debian/control (+32/-2)
debian/rules (+9/-8)
setup.py (+13/-7)
ubuntutools/archive.py (+40/-27)
ubuntutools/config.py (+1/-1)
ubuntutools/harvest.py (+10/-7)
ubuntutools/lp/libsupport.py (+9/-6)
ubuntutools/lp/lpapicache.py (+37/-14)
ubuntutools/misc.py (+10/-8)
ubuntutools/question.py (+24/-19)
ubuntutools/requestsync/lp.py (+16/-14)
ubuntutools/requestsync/mail.py (+15/-9)
ubuntutools/sponsor_patch/bugtask.py (+7/-3)
ubuntutools/sponsor_patch/patch.py (+1/-0)
ubuntutools/sponsor_patch/question.py (+3/-1)
ubuntutools/sponsor_patch/source_package.py (+11/-9)
ubuntutools/sponsor_patch/sponsor_patch.py (+14/-9)
ubuntutools/test/test_archive.py (+109/-134)
ubuntutools/test/test_config.py (+34/-13)
ubuntutools/test/test_help.py (+5/-1)
ubuntutools/test/test_logger.py (+6/-3)
ubuntutools/test/test_pylint.py (+0/-3)
ubuntutools/test/test_update_maintainer.py (+47/-28)
ubuntutools/update_maintainer.py (+11/-9)
To merge this branch: bzr merge lp:~xnox/ubuntu-dev-tools/py3k
Reviewer Review Type Date Requested Status
Barry Warsaw Pending
Ubuntu Development Team Pending
Review via email: mp+245250@code.launchpad.net

Description of the change

Port ubuntutools module & test suite to python3.

To post a comment you must log in.
lp:~xnox/ubuntu-dev-tools/py3k updated
1423. By Dimitri John Ledkov

There is no python3-soappy yet.

1424. By Dimitri John Ledkov

Mock mock more.

Revision history for this message
Dimitri John Ledkov (xnox) wrote :

I am pondering to land this in vivid (it will take time to go through new) and then make the library available and then continue to port the scripts.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'debian/changelog'
2--- debian/changelog 2014-07-25 07:50:59 +0000
3+++ debian/changelog 2014-12-19 22:54:27 +0000
4@@ -7,6 +7,9 @@
5 * mk-sbuild: better message for cross build so that new start have
6 correct sbuild command from the last message of mk-sbuild.
7
8+ [ Dimitri John Ledkov ]
9+ * Port ubuntutools module to python3.
10+
11 -- Logan Rosen <logan@ubuntu.com> Wed, 23 Apr 2014 17:24:12 -0400
12
13 ubuntu-dev-tools (0.153) unstable; urgency=medium
14
15=== modified file 'debian/control'
16--- debian/control 2014-01-06 21:48:08 +0000
17+++ debian/control 2014-12-19 22:54:27 +0000
18@@ -8,6 +8,7 @@
19 Vcs-Browser: https://code.launchpad.net/~ubuntu-dev/ubuntu-dev-tools/trunk
20 Build-Depends: dctrl-tools,
21 debhelper (>= 9),
22+ dh-python,
23 devscripts (>= 2.11.0~),
24 distro-info (>= 0.2~),
25 libwww-perl,
26@@ -19,11 +20,20 @@
27 python-distro-info (>= 0.4~),
28 python-httplib2,
29 python-launchpadlib (>= 1.5.7),
30- python-mox,
31 python-setuptools,
32 python-soappy,
33- python-unittest2
34+ python-unittest2,
35+ python-mock,
36+ python3-all,
37+ python3-apt,
38+ python3-debian,
39+ python3-distro-info,
40+ python3-httplib2,
41+ python3-launchpadlib,
42+ python3-setuptools,
43+ python3-mock,
44 X-Python-Version: >= 2.6
45+X-Python3-Version: >= 3.2
46 Homepage: https://launchpad.net/ubuntu-dev-tools
47 Standards-Version: 3.9.5
48
49@@ -114,3 +124,23 @@
50 - ubuntu-upload-permission - query / list the upload permissions for a
51 package.
52 - update-maintainer - script to update maintainer field in ubuntu packages.
53+
54+Package: python-ubuntutools
55+Architecture: all
56+Depends: ${python:Depends}
57+Breaks: ubuntu-dev-tools (<< 0.154)
58+Replaces: ubuntu-dev-tools (<< 0.154)
59+Description: useful library of APIs for Ubuntu developer tools (Python 2)
60+ This package ships a collection of APIs, helpers and wrappers used to
61+ develop useful utiliteis for Ubuntu developers.
62+ .
63+ Python 2 variant.
64+
65+Package: python3-ubuntutools
66+Architecture: all
67+Depends: ${python3:Depends}
68+Description: useful library of APIs for Ubuntu developer tools
69+ This package ships a collection of APIs, helpers and wrappers used to
70+ develop useful utiliteis for Ubuntu developers.
71+ .
72+ Python 3 variant.
73
74=== modified file 'debian/rules'
75--- debian/rules 2011-06-25 15:53:44 +0000
76+++ debian/rules 2014-12-19 22:54:27 +0000
77@@ -1,12 +1,13 @@
78 #!/usr/bin/make -f
79
80+export PYBUILD_NAME=ubuntutools
81+
82 %:
83- dh $@ --with python2
84+ dh $@ --with python2,python3 --buildsystem=pybuild
85
86-ifeq (,$(filter nocheck,$(DEB_BUILD_OPTIONS)))
87-override_dh_auto_test:
88- set -e; \
89- for python in $(shell pyversions -r); do \
90- $$python setup.py test; \
91- done
92-endif
93+override_dh_install:
94+ dh_install
95+ mkdir -p debian/ubuntu-dev-tools/usr
96+ mv debian/python-ubuntutools/etc debian/ubuntu-dev-tools
97+ mv debian/python-ubuntutools/usr/bin debian/ubuntu-dev-tools/usr/
98+ mv debian/python-ubuntutools/usr/share debian/ubuntu-dev-tools/usr/
99
100=== modified file 'setup.py'
101--- setup.py 2012-11-09 11:02:01 +0000
102+++ setup.py 2014-12-19 22:54:27 +0000
103@@ -4,6 +4,7 @@
104 import glob
105 import os
106 import re
107+import sys
108
109 # look/set what version we have
110 changelog = "debian/changelog"
111@@ -13,7 +14,11 @@
112 if match:
113 version = match.group(1)
114
115-scripts = ['404main',
116+if sys.version_info[0] >= 3:
117+ scripts = []
118+ data_files = []
119+else:
120+ scripts = ['404main',
121 'backportpackage',
122 'bitesize',
123 'check-mir',
124@@ -46,6 +51,12 @@
125 'ubuntu-upload-permission',
126 'update-maintainer',
127 ]
128+ data_files = [
129+ ('/etc/bash_completion.d', glob.glob("bash_completion/*")),
130+ ('share/man/man1', glob.glob("doc/*.1")),
131+ ('share/man/man5', glob.glob("doc/*.5")),
132+ ('share/ubuntu-dev-tools', ['enforced-editing-wrapper']),
133+ ]
134
135 if __name__ == '__main__':
136 setup(name='ubuntu-dev-tools',
137@@ -57,11 +68,6 @@
138 'ubuntutools/sponsor_patch',
139 'ubuntutools/test',
140 ],
141- data_files=[('/etc/bash_completion.d',
142- glob.glob("bash_completion/*")),
143- ('share/man/man1', glob.glob("doc/*.1")),
144- ('share/man/man5', glob.glob("doc/*.5")),
145- ('share/ubuntu-dev-tools', ['enforced-editing-wrapper']),
146- ],
147+ data_files=data_files,
148 test_suite='ubuntutools.test.discover',
149 )
150
151=== modified file 'ubuntutools/archive.py'
152--- ubuntutools/archive.py 2013-03-18 23:18:02 +0000
153+++ ubuntutools/archive.py 2014-12-19 22:54:27 +0000
154@@ -27,18 +27,28 @@
155 3. Verify checksums.
156 """
157
158-from __future__ import with_statement
159+from __future__ import with_statement, print_function
160
161 import hashlib
162 import os.path
163-import urllib2
164-import urlparse
165+try:
166+ from urllib.request import ProxyHandler, build_opener, urlopen
167+ from urllib.parse import urlparse
168+ from urllib.error import URLError, HTTPError
169+except ImportError:
170+ from urllib2 import ProxyHandler, build_opener, urlopen
171+ from urlparse import urlparse
172+ from urllib2 import URLError, HTTPError
173 import re
174 import sys
175+if sys.version_info[0] >= 3:
176+ basestring = str
177+ unicode = str
178
179 from debian.changelog import Changelog, Version
180 import debian.deb822
181 import debian.debian_support
182+import codecs
183 import httplib2
184
185 from ubuntutools.config import UDTConfig
186@@ -81,7 +91,7 @@
187 f = open(pathname, 'rb')
188 while True:
189 buf = f.read(hash_func.block_size)
190- if buf == '':
191+ if buf == b'':
192 break
193 hash_func.update(buf)
194 f.close()
195@@ -102,7 +112,7 @@
196 their_checksums = \
197 dict((entry['name'], (int(entry['size']), entry[key]))
198 for entry in other[field])
199- for name, (size, checksum) in our_checksums.iteritems():
200+ for name, (size, checksum) in our_checksums.items():
201 if name not in their_checksums:
202 # file only in one dsc
203 continue
204@@ -154,8 +164,8 @@
205 self.version = debian.debian_support.Version(version)
206
207 # uses default proxies from the environment
208- proxy = urllib2.ProxyHandler()
209- self.url_opener = urllib2.build_opener(proxy)
210+ proxy = ProxyHandler()
211+ self.url_opener = build_opener(proxy)
212
213 @property
214 def lp_spph(self):
215@@ -231,10 +241,10 @@
216 def pull_dsc(self):
217 "Retrieve dscfile and parse"
218 if self._dsc_source:
219- parsed = urlparse.urlparse(self._dsc_source)
220+ parsed = urlparse(self._dsc_source)
221 if parsed.scheme == '':
222 self._dsc_source = 'file://' + os.path.abspath(self._dsc_source)
223- parsed = urlparse.urlparse(self._dsc_source)
224+ parsed = urlparse(self._dsc_source)
225 url = self._dsc_source
226 else:
227 url = self._lp_url(self.dsc_name)
228@@ -244,14 +254,14 @@
229
230 def _download_dsc(self, url):
231 "Download specified dscfile and parse"
232- parsed = urlparse.urlparse(url)
233+ parsed = urlparse(url)
234 if parsed.scheme == 'file':
235 with open(parsed.path, 'r') as f:
236 body = f.read()
237 else:
238 try:
239 response, body = httplib2.Http().request(url)
240- except httplib2.HttpLib2Error, e:
241+ except httplib2.HttpLib2Error as e:
242 raise DownloadError(e)
243 if response.status != 200:
244 raise DownloadError("%s: %s %s" % (url, response.status,
245@@ -299,7 +309,7 @@
246 "Write dsc file to workdir"
247 if self._dsc is None:
248 self.pull_dsc()
249- with open(self.dsc_pathname, 'w') as f:
250+ with open(self.dsc_pathname, 'wb') as f:
251 f.write(self.dsc.raw_text)
252
253 def _download_file(self, url, filename):
254@@ -312,17 +322,17 @@
255 if entry['name'] == filename]
256 assert len(size) == 1
257 size = int(size[0])
258- parsed = urlparse.urlparse(url)
259+ parsed = urlparse(url)
260 if not self.quiet:
261 Logger.normal('Downloading %s from %s (%0.3f MiB)',
262 filename, parsed.hostname, size / 1024.0 / 1024)
263
264 if parsed.scheme == 'file':
265- in_ = open(parsed.path, 'r')
266+ in_ = open(parsed.path, 'rb')
267 else:
268 try:
269 in_ = self.url_opener.open(url)
270- except urllib2.URLError:
271+ except URLError:
272 return False
273
274 downloaded = 0
275@@ -331,10 +341,10 @@
276 with open(pathname, 'wb') as out:
277 while True:
278 block = in_.read(10240)
279- if block == '':
280+ if block == b'':
281 break
282 downloaded += len(block)
283- out.write(block)
284+ out.write(block)
285 if not self.quiet:
286 percent = downloaded * 100 // size
287 bar = '=' * int(round(downloaded * bar_width / size))
288@@ -360,9 +370,9 @@
289 try:
290 if self._download_file(url, name):
291 break
292- except urllib2.HTTPError, e:
293+ except HTTPError as e:
294 Logger.normal('HTTP Error %i: %s', e.code, str(e))
295- except urllib2.URLError, e:
296+ except URLError as e:
297 Logger.normal('URL Error: %s', e.reason)
298 else:
299 raise DownloadError('File %s could not be found' % name)
300@@ -457,7 +467,7 @@
301 wrapped_iterator = super(DebianSourcePackage, self)._source_urls(name)
302 while True:
303 try:
304- yield wrapped_iterator.next()
305+ yield next(wrapped_iterator)
306 except StopIteration:
307 break
308 if self.snapshot_list:
309@@ -499,11 +509,14 @@
310 "python-simplejson")
311
312 try:
313- srcfiles = json.load(self.url_opener.open(
314+ data = self.url_opener.open(
315 'http://snapshot.debian.org'
316 '/mr/package/%s/%s/srcfiles?fileinfo=1'
317- % (self.source, self.version.full_version)))
318- except urllib2.HTTPError:
319+ % (self.source, self.version.full_version))
320+ reader = codecs.getreader('utf-8')
321+ srcfiles = json.load(reader(data))
322+
323+ except HTTPError:
324 Logger.error('Version %s of %s not found on '
325 'snapshot.debian.org',
326 self.version.full_version, self.source)
327@@ -511,7 +524,7 @@
328 return False
329 self._snapshot_list = dict((info[0]['name'], hash_)
330 for hash_, info
331- in srcfiles['fileinfo'].iteritems())
332+ in srcfiles['fileinfo'].items())
333 return self._snapshot_list
334
335 def _snapshot_url(self, name):
336@@ -569,9 +582,9 @@
337 self.name + '_' + pkgversion,
338 'changelog' + extension)
339 try:
340- self._changelog = urllib2.urlopen(url).read()
341- except urllib2.HTTPError, error:
342- print >> sys.stderr, ('%s: %s' % (url, error))
343+ self._changelog = urlopen(url).read()
344+ except HTTPError as error:
345+ print(('%s: %s' % (url, error)), file=sys.stderr)
346 return None
347
348 if since_version is None:
349
350=== modified file 'ubuntutools/config.py'
351--- ubuntutools/config.py 2013-08-13 21:01:32 +0000
352+++ ubuntutools/config.py 2014-12-19 22:54:27 +0000
353@@ -179,6 +179,6 @@
354 encoding = locale.getdefaultlocale()[1]
355 if not encoding:
356 encoding = 'utf-8'
357- if name:
358+ if name and isinstance(name, bytes):
359 name = name.decode(encoding)
360 return name, email
361
362=== modified file 'ubuntutools/harvest.py'
363--- ubuntutools/harvest.py 2013-03-18 23:18:02 +0000
364+++ ubuntutools/harvest.py 2014-12-19 22:54:27 +0000
365@@ -14,7 +14,12 @@
366 import json
367 import os.path
368 import sys
369-import urllib2
370+try:
371+ from urllib.request import urlopen
372+ from urllib.error import URLError
373+except ImportError:
374+ from urllib2 import urlopen
375+ from urllib2 import URLError
376
377 from ubuntutools.logger import Logger
378
379@@ -32,11 +37,11 @@
380
381 def _get_data(self):
382 try:
383- sock = urllib2.urlopen(self.data_url)
384+ sock = urlopen(self.data_url)
385 except IOError:
386 try:
387- urllib2.urlopen(BASE_URL)
388- except urllib2.URLError:
389+ urlopen(BASE_URL)
390+ except URLError:
391 Logger.error("Harvest is down.")
392 sys.exit(1)
393 return None
394@@ -45,9 +50,7 @@
395 return json.loads(response)
396
397 def opportunity_summary(self):
398- l = []
399- for key in filter(lambda a: a != "total", self.data.keys()):
400- l += ["%s (%s)" % (key, self.data[key])]
401+ l = ["%s (%s)" % (k,v) for (k,v) in self.data.items() if k != "total"]
402 return ", ".join(l)
403
404 def report(self):
405
406=== modified file 'ubuntutools/lp/libsupport.py'
407--- ubuntutools/lp/libsupport.py 2011-02-28 22:32:36 +0000
408+++ ubuntutools/lp/libsupport.py 2014-12-19 22:54:27 +0000
409@@ -19,8 +19,11 @@
410 #
411
412 # Modules.
413-import urllib
414-import urlparse
415+try:
416+ from urllib.parse import urlsplit, urlencode, urlunsplit
417+except ImportError:
418+ from urllib import urlencode
419+ from urlparse import urlsplit, urlunsplit
420
421 def query_to_dict(query_string):
422 result = dict()
423@@ -31,7 +34,7 @@
424 return result
425
426 def translate_web_api(url, launchpad):
427- scheme, netloc, path, query, fragment = urlparse.urlsplit(url)
428+ scheme, netloc, path, query, fragment = urlsplit(url)
429 query = query_to_dict(query)
430
431 differences = set(netloc.split('.')).symmetric_difference(
432@@ -44,8 +47,8 @@
433 if "ws.op" in query:
434 raise ValueError("Invalid web url, url: %s" %url)
435 query["ws.op"] = "searchTasks"
436- scheme, netloc, api_path, _, _ = urlparse.urlsplit(str(launchpad._root_uri))
437- query = urllib.urlencode(query)
438- url = urlparse.urlunsplit((scheme, netloc, api_path + path.lstrip("/"),
439+ scheme, netloc, api_path, _, _ = urlsplit(str(launchpad._root_uri))
440+ query = urlencode(query)
441+ url = urlunsplit((scheme, netloc, api_path + path.lstrip("/"),
442 query, fragment))
443 return url
444
445=== modified file 'ubuntutools/lp/lpapicache.py'
446--- ubuntutools/lp/lpapicache.py 2012-07-26 19:40:49 +0000
447+++ ubuntutools/lp/lpapicache.py 2014-12-19 22:54:27 +0000
448@@ -21,12 +21,34 @@
449 #
450 # Based on code written by Jonathan Davies <jpds@ubuntu.com>
451
452+from __future__ import print_function
453+
454 # Uncomment for tracing LP API calls
455 #import httplib2
456 #httplib2.debuglevel = 1
457
458 import sys
459
460+if sys.version_info[0] >= 3:
461+ basestring = str
462+ unicode = str
463+
464+#Shameless steal from python-six
465+def add_metaclass(metaclass):
466+ """Class decorator for creating a class with a metaclass."""
467+ def wrapper(cls):
468+ orig_vars = cls.__dict__.copy()
469+ slots = orig_vars.get('__slots__')
470+ if slots is not None:
471+ if isinstance(slots, str):
472+ slots = [slots]
473+ for slots_var in slots:
474+ orig_vars.pop(slots_var)
475+ orig_vars.pop('__dict__', None)
476+ orig_vars.pop('__weakref__', None)
477+ return metaclass(cls.__name__, cls.__bases__, orig_vars)
478+ return wrapper
479+
480 from debian.changelog import Changelog, Version
481 from httplib2 import Http, HttpLib2Error
482 from launchpadlib.launchpad import Launchpad as LP
483@@ -39,6 +61,7 @@
484 PackageNotFoundException,
485 PocketDoesNotExistError,
486 SeriesNotFoundException)
487+import collections
488
489 __all__ = [
490 'Archive',
491@@ -64,8 +87,8 @@
492 try:
493 self.__lp = LP.login_with('ubuntu-dev-tools', service,
494 version=api_version)
495- except IOError, error:
496- print >> sys.stderr, 'E: %s' % error
497+ except IOError as error:
498+ print('E: %s' % error, file=sys.stderr)
499 raise
500 else:
501 raise AlreadyLoggedInError('Already logged in to Launchpad.')
502@@ -112,11 +135,11 @@
503 cls._cache = dict()
504
505
506+@add_metaclass(MetaWrapper)
507 class BaseWrapper(object):
508 '''
509 A base class from which other wrapper classes are derived.
510 '''
511- __metaclass__ = MetaWrapper
512 resource_type = None # it's a base class after all
513
514 def __new__(cls, data):
515@@ -149,7 +172,7 @@
516 cls._cache[data.self_link] = cached
517 # add additional class specific caching (if available)
518 cache = getattr(cls, 'cache', None)
519- if callable(cache):
520+ if isinstance(cache, collections.Callable):
521 cache(cached)
522 return cached
523 else:
524@@ -158,7 +181,7 @@
525 else:
526 # not a LP API representation, let the specific class handle it
527 fetch = getattr(cls, 'fetch', None)
528- if callable(fetch):
529+ if isinstance(fetch, collections.Callable):
530 return fetch(data)
531 else:
532 raise NotImplementedError("Don't know how to fetch '%s' from LP"
533@@ -502,19 +525,19 @@
534 if self._changelog is None:
535 url = self._lpobject.changelogUrl()
536 if url is None:
537- print >> sys.stderr, ('E: No changelog available for %s %s',
538+ print(('E: No changelog available for %s %s',
539 (self.getPackageName(),
540- self.getVersion()))
541+ self.getVersion())), file=sys.stderr)
542 return None
543
544 try:
545 response, changelog = Http().request(url)
546- except HttpLib2Error, e:
547- print >> sys.stderr, str(e)
548+ except HttpLib2Error as e:
549+ print(str(e), file=sys.stderr)
550 return None
551 if response.status != 200:
552- print >> sys.stderr, ('%s: %s %s' % (url, response.status,
553- response.reason))
554+ print(('%s: %s %s' % (url, response.status,
555+ response.reason)), file=sys.stderr)
556 return None
557 self._changelog = changelog
558
559@@ -627,7 +650,7 @@
560 if '_me' not in cls.__dict__:
561 try:
562 cls._me = PersonTeam(Launchpad.me)
563- except HTTPError, error:
564+ except HTTPError as error:
565 if error.response.status == 401:
566 # Anonymous login
567 cls._me = None
568@@ -636,11 +659,11 @@
569 return cls._me
570
571
572+@add_metaclass(MetaPersonTeam)
573 class PersonTeam(BaseWrapper):
574 '''
575 Wrapper class around a LP person or team object.
576 '''
577- __metaclass__ = MetaPersonTeam
578
579 resource_type = (
580 'person',
581@@ -716,7 +739,7 @@
582 sourcepackagename=package,
583 )
584 canUpload = True
585- except HTTPError, e:
586+ except HTTPError as e:
587 if e.response.status == 403:
588 canUpload = False
589 else:
590
591=== modified file 'ubuntutools/misc.py'
592--- ubuntutools/misc.py 2011-11-22 23:45:49 +0000
593+++ ubuntutools/misc.py 2014-12-19 22:54:27 +0000
594@@ -22,6 +22,8 @@
595 #
596 # ##################################################################
597
598+from __future__ import print_function
599+
600 # Modules.
601 import locale
602 import os
603@@ -66,8 +68,8 @@
604 break
605 _system_distribution_chain.append(parent)
606 except Exception:
607- print ('Error: Could not determine the parent of the '
608- 'distribution %s' % _system_distribution_chain[-1])
609+ print(('Error: Could not determine the parent of the '
610+ 'distribution %s' % _system_distribution_chain[-1]))
611 return []
612
613 return _system_distribution_chain
614@@ -92,8 +94,8 @@
615 stderr=PIPE).communicate()[0].split()
616
617 if not arch or 'not found' in arch[0]:
618- print 'Error: Not running on a Debian based system; could not ' \
619- 'detect its architecture.'
620+ print('Error: Not running on a Debian based system; could not ' \
621+ 'detect its architecture.')
622 return None
623
624 return arch[0]
625@@ -106,13 +108,13 @@
626 """
627
628 if not os.path.isfile(filename):
629- print 'File "%s" does not exist.' % filename
630+ print('File "%s" does not exist.' % filename)
631 return False
632
633 content = open(filename).read().replace('\n', ' ').replace(',', ' ')
634
635 if not content.strip():
636- print 'File "%s" is empty.' % filename
637+ print('File "%s" is empty.' % filename)
638 return False
639
640 items = [item for item in content.split() if item]
641@@ -149,8 +151,8 @@
642 def require_utf8():
643 '''Can be called by programs that only function in UTF-8 locales'''
644 if locale.getpreferredencoding() != 'UTF-8':
645- print >> sys.stderr, ("This program only functions in a UTF-8 locale. "
646- "Aborting.")
647+ print(("This program only functions in a UTF-8 locale. "
648+ "Aborting."), file=sys.stderr)
649 sys.exit(1)
650
651
652
653=== modified file 'ubuntutools/question.py'
654--- ubuntutools/question.py 2012-11-08 08:10:26 +0000
655+++ ubuntutools/question.py 2014-12-19 22:54:27 +0000
656@@ -16,10 +16,15 @@
657 # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
658 # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
659
660+from __future__ import print_function
661+
662 import tempfile
663 import os
664 import re
665 import sys
666+if sys.version_info[0] < 3:
667+ input = raw_input
668+
669
670 import ubuntutools.subprocess
671
672@@ -56,9 +61,9 @@
673 selected = None
674 while selected not in self.options:
675 try:
676- selected = raw_input(question).strip().lower()
677+ selected = input(question).strip().lower()
678 except (EOFError, KeyboardInterrupt):
679- print '\nAborting as requested.'
680+ print('\nAborting as requested.')
681 sys.exit(1)
682 if selected == "":
683 selected = default
684@@ -68,8 +73,8 @@
685 if selected == option[0]:
686 selected = option
687 if selected not in self.options:
688- print "Please answer the question with " + \
689- self.get_options() + "."
690+ print("Please answer the question with " + \
691+ self.get_options() + ".")
692 return selected
693
694
695@@ -86,9 +91,9 @@
696 selected = None
697 while selected < min_number or selected > max_number:
698 try:
699- selected = raw_input(question).strip()
700+ selected = input(question).strip()
701 except (EOFError, KeyboardInterrupt):
702- print '\nAborting as requested.'
703+ print('\nAborting as requested.')
704 sys.exit(1)
705 if default and selected == "":
706 selected = default
707@@ -96,10 +101,10 @@
708 try:
709 selected = int(selected)
710 if selected < min_number or selected > max_number:
711- print "Please input a number between %i and %i." % \
712- (min_number, max_number)
713+ print("Please input a number between %i and %i." % \
714+ (min_number, max_number))
715 except ValueError:
716- print "Please input a number."
717+ print("Please input a number.")
718 assert type(selected) == int
719 return selected
720
721@@ -113,9 +118,9 @@
722 action = 'continue'
723 message = 'Press [Enter] to %s. Press [Ctrl-C] to abort now.' % action
724 try:
725- raw_input(message)
726+ input(message)
727 except (EOFError, KeyboardInterrupt):
728- print '\nAborting as requested.'
729+ print('\nAborting as requested.')
730 sys.exit(1)
731
732
733@@ -129,9 +134,9 @@
734
735 def edit(self, optional=False):
736 if optional:
737- print "\n\nCurrently the %s looks like:" % self.description
738+ print("\n\nCurrently the %s looks like:" % self.description)
739 with open(self.filename, 'r') as f:
740- print f.read()
741+ print(f.read())
742 if YesNoQuestion().ask("Edit", "no") == "no":
743 return
744
745@@ -150,12 +155,12 @@
746 placeholders_present = True
747
748 if placeholders_present:
749- print ("Placeholders still present in the %s. "
750- "Please replace them with useful information."
751- % self.description)
752+ print("Placeholders still present in the %s. "
753+ "Please replace them with useful information."
754+ % self.description)
755 confirmation_prompt(action='edit again')
756 elif not modified:
757- print "The %s was not modified" % self.description
758+ print("The %s was not modified" % self.description)
759 if YesNoQuestion().ask("Edit again", "yes") == "no":
760 done = True
761 elif self.check_edit():
762@@ -189,8 +194,8 @@
763 report = f.read().decode('utf-8')
764
765 if self.split_re.match(report) is None:
766- print ("The %s doesn't start with 'Summary:' and 'Description:' "
767- "blocks" % self.description)
768+ print("The %s doesn't start with 'Summary:' and 'Description:' "
769+ "blocks" % self.description)
770 confirmation_prompt('edit again')
771 return False
772 return True
773
774=== modified file 'ubuntutools/requestsync/lp.py'
775--- ubuntutools/requestsync/lp.py 2013-03-18 23:18:02 +0000
776+++ ubuntutools/requestsync/lp.py 2014-12-19 22:54:27 +0000
777@@ -20,6 +20,8 @@
778 # Please see the /usr/share/common-licenses/GPL-2 file for the full text
779 # of the GNU General Public License license.
780
781+from __future__ import print_function
782+
783 import re
784
785 from debian.deb822 import Changes
786@@ -39,7 +41,7 @@
787
788 try:
789 release = DebianDistroInfo().codename(release, None, release)
790- except DistroDataOutdated, e:
791+ except DistroDataOutdated as e:
792 Logger.warn(e)
793
794 return debian_archive.getSourcePackage(name, release)
795@@ -71,11 +73,11 @@
796 need_sponsor = not PersonTeam.me.canUploadPackage(archive, distroseries,
797 name, component)
798 if need_sponsor:
799- print '''You are not able to upload this package directly to Ubuntu.
800+ print('''You are not able to upload this package directly to Ubuntu.
801 Your sync request shall require an approval by a member of the appropriate
802 sponsorship team, who shall be subscribed to this bug report.
803 This must be done before it can be processed by a member of the Ubuntu Archive
804-team.'''
805+team.''')
806 confirmation_prompt()
807
808 return need_sponsor
809@@ -98,12 +100,12 @@
810 for bug in pkg_bug_list:
811 # check for Sync or sync and the package name
812 if not bug.is_complete and 'ync %s' % srcpkg in bug.title:
813- print ('The following bug could be a possible duplicate sync bug '
814- 'on Launchpad:\n'
815- ' * %s (%s)\n'
816- 'Please check the above URL to verify this before '
817- 'continuing.'
818- % (bug.title, bug.web_link))
819+ print('The following bug could be a possible duplicate sync bug '
820+ 'on Launchpad:\n'
821+ ' * %s (%s)\n'
822+ 'Please check the above URL to verify this before '
823+ 'continuing.'
824+ % (bug.title, bug.web_link))
825 confirmation_prompt()
826
827 def get_ubuntu_delta_changelog(srcpkg):
828@@ -127,7 +129,7 @@
829 break
830 try:
831 response, body = Http().request(changes_url)
832- except HttpLib2Error, e:
833+ except HttpLib2Error as e:
834 Logger.error(str(e))
835 break
836 if response.status != 200:
837@@ -156,8 +158,8 @@
838 Use the LP API to file the sync request.
839 '''
840
841- print ('The final report is:\nSummary: %s\nDescription:\n%s\n'
842- % (bugtitle, bugtext))
843+ print('The final report is:\nSummary: %s\nDescription:\n%s\n'
844+ % (bugtitle, bugtext))
845 confirmation_prompt()
846
847 if srcpkg:
848@@ -181,5 +183,5 @@
849
850 bug.subscribe(person = PersonTeam(subscribe)())
851
852- print ('Sync request filed as bug #%i: %s'
853- % (bug.id, bug.web_link))
854+ print('Sync request filed as bug #%i: %s'
855+ % (bug.id, bug.web_link))
856
857=== modified file 'ubuntutools/requestsync/mail.py'
858--- ubuntutools/requestsync/mail.py 2013-03-18 23:18:02 +0000
859+++ ubuntutools/requestsync/mail.py 2014-12-19 22:54:27 +0000
860@@ -20,6 +20,8 @@
861 # Please see the /usr/share/common-licenses/GPL-2 file for the full text
862 # of the GNU General Public License license.
863
864+from __future__ import print_function
865+
866 import os
867 import re
868 import sys
869@@ -27,6 +29,10 @@
870 import socket
871 import tempfile
872
873+if sys.version_info[0] >= 3:
874+ basestring = str
875+ unicode = str
876+
877 from debian.changelog import Changelog, Version
878 from distro_info import DebianDistroInfo, DistroDataOutdated
879
880@@ -51,7 +57,7 @@
881 debian_info = DebianDistroInfo()
882 try:
883 release = debian_info.codename(release, default=release)
884- except DistroDataOutdated, e:
885+ except DistroDataOutdated as e:
886 Logger.warn(e)
887
888 lines = list(rmadison(distro, name, suite=release, arch='source'))
889@@ -86,9 +92,9 @@
890 '''
891 Point the user to the URL to manually check for duplicate bug reports.
892 '''
893- print ('Please check on '
894- 'https://bugs.launchpad.net/ubuntu/+source/%s/+bugs\n'
895- 'for duplicate sync requests before continuing.' % srcpkg)
896+ print('Please check on '
897+ 'https://bugs.launchpad.net/ubuntu/+source/%s/+bugs\n'
898+ 'for duplicate sync requests before continuing.' % srcpkg)
899 confirmation_prompt()
900
901 def get_ubuntu_delta_changelog(srcpkg):
902@@ -164,7 +170,7 @@
903
904 %s''' % (myemailaddr, to, bugtitle, signed_report)
905
906- print 'The final report is:\n%s' % mail
907+ print('The final report is:\n%s' % mail)
908 confirmation_prompt()
909
910 # save mail in temporary file
911@@ -184,11 +190,11 @@
912 mailserver_port)
913 s = smtplib.SMTP(mailserver_host, mailserver_port)
914 break
915- except socket.error, s:
916+ except socket.error as s:
917 Logger.error('Could not connect to %s:%s: %s (%i)',
918 mailserver_host, mailserver_port, s[1], s[0])
919 return
920- except smtplib.SMTPConnectError, s:
921+ except smtplib.SMTPConnectError as s:
922 Logger.error('Could not connect to %s:%s: %s (%i)',
923 mailserver_host, mailserver_port, s[1], s[0])
924 if s.smtp_code == 421:
925@@ -215,7 +221,7 @@
926 os.remove(backup.name)
927 Logger.normal('Sync request mailed.')
928 break
929- except smtplib.SMTPRecipientsRefused, smtperror:
930+ except smtplib.SMTPRecipientsRefused as smtperror:
931 smtp_code, smtp_message = smtperror.recipients[to]
932 Logger.error('Error while sending: %i, %s', smtp_code, smtp_message)
933 if smtp_code == 450:
934@@ -223,7 +229,7 @@
935 '[Enter] to retry. Press [Ctrl-C] to abort now.')
936 else:
937 return
938- except smtplib.SMTPResponseException, e:
939+ except smtplib.SMTPResponseException as e:
940 Logger.error('Error while sending: %i, %s',
941 e.smtp_code, e.smtp_error)
942 return
943
944=== modified file 'ubuntutools/sponsor_patch/bugtask.py'
945--- ubuntutools/sponsor_patch/bugtask.py 2013-03-18 23:18:02 +0000
946+++ ubuntutools/sponsor_patch/bugtask.py 2014-12-19 22:54:27 +0000
947@@ -17,7 +17,11 @@
948
949 import os
950 import re
951-import urllib
952+try:
953+ from urllib.parse import unquote
954+ from urllib.request import urlretrieve
955+except ImportError:
956+ from urllib import unquote, urlretrieve
957
958 import debian.debian_support
959 import distro_info
960@@ -63,7 +67,7 @@
961 source_files = self.get_source().sourceFileUrls()
962 dsc_file = ""
963 for url in source_files:
964- filename = urllib.unquote(os.path.basename(url))
965+ filename = unquote(os.path.basename(url))
966 Logger.info("Downloading %s..." % (filename))
967 # HttpLib2 isn't suitable for large files (it reads into memory),
968 # but we want its https certificate validation on the .dsc
969@@ -75,7 +79,7 @@
970
971 dsc_file = os.path.join(os.getcwd(), filename)
972 else:
973- urllib.urlretrieve(url, filename)
974+ urlretrieve(url, filename)
975 assert os.path.isfile(dsc_file), "%s does not exist." % (dsc_file)
976 return dsc_file
977
978
979=== modified file 'ubuntutools/sponsor_patch/patch.py'
980--- ubuntutools/sponsor_patch/patch.py 2013-03-18 23:18:02 +0000
981+++ ubuntutools/sponsor_patch/patch.py 2014-12-19 22:54:27 +0000
982@@ -21,6 +21,7 @@
983 from ubuntutools import subprocess
984 from ubuntutools.logger import Logger
985 from ubuntutools.sponsor_patch.question import ask_for_manual_fixing
986+from functools import reduce
987
988 class Patch(object):
989 """This object represents a patch that can be downloaded from Launchpad."""
990
991=== modified file 'ubuntutools/sponsor_patch/question.py'
992--- ubuntutools/sponsor_patch/question.py 2011-12-21 21:09:53 +0000
993+++ ubuntutools/sponsor_patch/question.py 2014-12-19 22:54:27 +0000
994@@ -15,6 +15,8 @@
995 # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
996 # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
997
998+from __future__ import print_function
999+
1000 import sys
1001
1002 from ubuntutools.question import Question, YesNoQuestion
1003@@ -43,5 +45,5 @@
1004 def user_abort():
1005 """Print abort and quit the program."""
1006
1007- print "User abort."
1008+ print("User abort.")
1009 sys.exit(2)
1010
1011=== modified file 'ubuntutools/sponsor_patch/source_package.py'
1012--- ubuntutools/sponsor_patch/source_package.py 2013-03-18 23:18:02 +0000
1013+++ ubuntutools/sponsor_patch/source_package.py 2014-12-19 22:54:27 +0000
1014@@ -15,6 +15,8 @@
1015 # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1016 # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1017
1018+from __future__ import print_function
1019+
1020 import os
1021 import re
1022 import sys
1023@@ -301,7 +303,7 @@
1024 bug title."""
1025
1026 if not task.title_contains(self._version):
1027- print "Bug #%i title: %s" % (bug_number, task.get_bug_title())
1028+ print("Bug #%i title: %s" % (bug_number, task.get_bug_title()))
1029 msg = "Is %s %s the version that should be synced" % (self._package,
1030 self._version)
1031 answer = YesNoQuestion().ask(msg, "no")
1032@@ -349,7 +351,7 @@
1033
1034 assert os.path.isfile(self._changes_file), "%s does not exist." % \
1035 (self._changes_file)
1036- changes = debian.deb822.Changes(file(self._changes_file))
1037+ changes = debian.deb822.Changes(open(self._changes_file))
1038 fixed_bugs = []
1039 if "Launchpad-Bugs-Fixed" in changes:
1040 fixed_bugs = changes["Launchpad-Bugs-Fixed"].split(" ")
1041@@ -370,16 +372,16 @@
1042 """Print things that should be checked before uploading a package."""
1043
1044 lintian_filename = self._run_lintian()
1045- print "\nPlease check %s %s carefully:" % (self._package, self._version)
1046+ print("\nPlease check %s %s carefully:" % (self._package, self._version))
1047 if os.path.isfile(self._debdiff_filename):
1048- print "file://" + self._debdiff_filename
1049- print "file://" + lintian_filename
1050+ print("file://" + self._debdiff_filename)
1051+ print("file://" + lintian_filename)
1052 if self._build_log:
1053- print "file://" + self._build_log
1054+ print("file://" + self._build_log)
1055
1056 harvest = Harvest(self._package)
1057 if harvest.data:
1058- print harvest.report()
1059+ print(harvest.report())
1060
1061 def reload_changelog(self):
1062 """Reloads debian/changelog and updates the version.
1063@@ -391,9 +393,9 @@
1064 # Check the changelog
1065 self._changelog = debian.changelog.Changelog()
1066 try:
1067- self._changelog.parse_changelog(file("debian/changelog"),
1068+ self._changelog.parse_changelog(open("debian/changelog"),
1069 max_blocks=1, strict=True)
1070- except debian.changelog.ChangelogParseError, error:
1071+ except debian.changelog.ChangelogParseError as error:
1072 Logger.error("The changelog entry doesn't validate: %s", str(error))
1073 ask_for_manual_fixing()
1074 return False
1075
1076=== modified file 'ubuntutools/sponsor_patch/sponsor_patch.py'
1077--- ubuntutools/sponsor_patch/sponsor_patch.py 2013-08-22 15:22:55 +0000
1078+++ ubuntutools/sponsor_patch/sponsor_patch.py 2014-12-19 22:54:27 +0000
1079@@ -15,11 +15,16 @@
1080 # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1081 # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1082
1083+from __future__ import print_function
1084+
1085 import os
1086 import pwd
1087 import shutil
1088 import sys
1089
1090+if sys.version_info[0] < 3:
1091+ range = xrange
1092+
1093 from distro_info import UbuntuDistroInfo
1094
1095 from launchpadlib.launchpad import Launchpad
1096@@ -81,11 +86,11 @@
1097 # Spawn shell to allow modifications
1098 cmd = [get_user_shell()]
1099 Logger.command(cmd)
1100- print """An interactive shell was launched in
1101+ print("""An interactive shell was launched in
1102 file://%s
1103 Edit your files. When you are done, exit the shell. If you wish to abort the
1104 process, exit the shell such that it returns an exit code other than zero.
1105-""" % (os.getcwd()),
1106+""" % (os.getcwd()), end=' ')
1107 returncode = subprocess.call(cmd)
1108 if returncode != 0:
1109 Logger.error("Shell exited with exit value %i." % (returncode))
1110@@ -113,10 +118,10 @@
1111 i = 0
1112 for linked_branch in linked_branches:
1113 i += 1
1114- print "%i) %s" % (i, linked_branch.display_name)
1115+ print("%i) %s" % (i, linked_branch.display_name))
1116 for attached_patch in attached_patches:
1117 i += 1
1118- print "%i) %s" % (i, attached_patch.title)
1119+ print("%i) %s" % (i, attached_patch.title))
1120 selected = input_number("Which branch or patch do you want to download",
1121 1, i, i)
1122 if selected <= len(linked_branches):
1123@@ -220,9 +225,9 @@
1124 else:
1125 Logger.normal("https://launchpad.net/bugs/%i has %i Ubuntu tasks:" \
1126 % (bug_id, len(ubuntu_tasks)))
1127- for i in xrange(len(ubuntu_tasks)):
1128- print "%i) %s" % (i + 1,
1129- ubuntu_tasks[i].get_package_and_series())
1130+ for i in range(len(ubuntu_tasks)):
1131+ print("%i) %s" % (i + 1,
1132+ ubuntu_tasks[i].get_package_and_series()))
1133 selected = input_number("To which Ubuntu task does the patch belong",
1134 1, len(ubuntu_tasks))
1135 task = ubuntu_tasks[selected - 1]
1136@@ -235,7 +240,7 @@
1137 if not os.path.isdir(workdir):
1138 try:
1139 os.makedirs(workdir)
1140- except os.error, error:
1141+ except os.error as error:
1142 Logger.error("Failed to create the working directory %s [Errno " \
1143 "%i]: %s." % (workdir, error.errno, error.strerror))
1144 sys.exit(1)
1145@@ -248,7 +253,7 @@
1146 Logger.command(["update-maintainer"])
1147 try:
1148 update_maintainer("debian", Logger.verbose)
1149- except MaintainerUpdateException, e:
1150+ except MaintainerUpdateException as e:
1151 Logger.error("update-maintainer failed: %s", str(e))
1152 sys.exit(1)
1153
1154
1155=== modified file 'ubuntutools/test/test_archive.py'
1156--- ubuntutools/test/test_archive.py 2014-02-25 20:46:10 +0000
1157+++ ubuntutools/test/test_archive.py 2014-12-19 22:54:27 +0000
1158@@ -14,19 +14,30 @@
1159 # OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
1160 # PERFORMANCE OF THIS SOFTWARE.
1161
1162-from __future__ import with_statement
1163
1164-import __builtin__
1165+try:
1166+ import builtins
1167+except ImportError:
1168+ import __builtin__
1169 import os.path
1170 import shutil
1171-import StringIO
1172+try:
1173+ from StringIO import StringIO
1174+except:
1175+ from io import StringIO
1176+from io import BytesIO
1177 import tempfile
1178 import types
1179-import urllib2
1180-
1181+try:
1182+ from urllib.request import OpenerDirector, urlopen
1183+ from urllib.error import HTTPError, URLError
1184+except ImportError:
1185+ from urllib2 import OpenerDirector, urlopen
1186+ from urllib2 import HTTPError, URLError
1187 import debian.deb822
1188 import httplib2
1189-import mox
1190+import sys
1191+import mock
1192
1193 import ubuntutools.archive
1194 from ubuntutools.config import UDTConfig
1195@@ -44,15 +55,11 @@
1196 ex_pkg.cleanup()
1197
1198
1199-class DscVerificationTestCase(mox.MoxTestBase, unittest.TestCase):
1200+class DscVerificationTestCase(unittest.TestCase):
1201 def setUp(self):
1202- super(DscVerificationTestCase, self).setUp()
1203 with open('test-data/example_1.0-1.dsc', 'rb') as f:
1204 self.dsc = ubuntutools.archive.Dsc(f.read())
1205
1206- def tearDown(self):
1207- super(DscVerificationTestCase, self).tearDown()
1208-
1209 def test_good(self):
1210 self.assertTrue(self.dsc.verify_file(
1211 'test-data/example_1.0.orig.tar.gz'))
1212@@ -67,11 +74,19 @@
1213 fn = 'test-data/example_1.0.orig.tar.gz'
1214 with open(fn, 'rb') as f:
1215 data = f.read()
1216- data = data[:-1] + chr(ord(data[-1]) ^ 8)
1217- self.mox.StubOutWithMock(__builtin__, 'open')
1218- open(fn, 'rb').AndReturn(StringIO.StringIO(data))
1219- self.mox.ReplayAll()
1220- self.assertFalse(self.dsc.verify_file(fn))
1221+ if sys.version_info[0] >= 3:
1222+ last_byte = chr(data[-1] ^ 8).encode()
1223+ else:
1224+ last_byte = chr(ord(data[-1]) ^ 8)
1225+ data = data[:-1] + last_byte
1226+ m = mock.MagicMock(name='open', spec=open)
1227+ m.return_value = BytesIO(data)
1228+ if sys.version_info[0] >= 3:
1229+ target = 'builtins.open'
1230+ else:
1231+ target = '__builtin__.open'
1232+ with mock.patch(target, m):
1233+ self.assertFalse(self.dsc.verify_file(fn))
1234
1235 def test_sha1(self):
1236 del self.dsc['Checksums-Sha256']
1237@@ -85,26 +100,31 @@
1238 self.test_bad()
1239
1240
1241-class LocalSourcePackageTestCase(mox.MoxTestBase, unittest.TestCase):
1242+class LocalSourcePackageTestCase(unittest.TestCase):
1243 SourcePackage = ubuntutools.archive.UbuntuSourcePackage
1244
1245 def setUp(self):
1246- super(LocalSourcePackageTestCase, self).setUp()
1247 self.workdir = tempfile.mkdtemp(prefix='udt-test')
1248
1249- self.mox.StubOutWithMock(ubuntutools.archive, 'Distribution')
1250- self.mox.StubOutWithMock(ubuntutools.archive, 'rmadison')
1251-
1252- self.real_http = httplib2.Http()
1253- self.mox.StubOutWithMock(httplib2, 'Http')
1254- self.mock_http = self.mox.CreateMock(httplib2.Http)
1255+ self._stubout('ubuntutools.archive.Distribution')
1256+ self._stubout('ubuntutools.archive.rmadison')
1257+
1258+ self.mock_http = self._stubout('httplib2.Http.request')
1259+ self.mock_http.side_effect = self.request_proxy
1260+
1261+ self.url_opener = mock.MagicMock(spec=OpenerDirector)
1262+ self.url_opener.open.side_effect = self.urlopen_proxy
1263
1264 # Silence the tests a little:
1265- self.mox.stubs.Set(Logger, 'stdout', StringIO.StringIO())
1266- self.mox.stubs.Set(Logger, 'stderr', StringIO.StringIO())
1267+ self._stubout('ubuntutools.logger.Logger.stdout')
1268+ self._stubout('ubuntutools.logger.Logger.stderr')
1269+
1270+ def _stubout(self, stub):
1271+ patcher = mock.patch(stub)
1272+ self.addCleanup(patcher.stop)
1273+ return patcher.start()
1274
1275 def tearDown(self):
1276- super(LocalSourcePackageTestCase, self).tearDown()
1277 shutil.rmtree(self.workdir)
1278
1279 def urlopen_proxy(self, url, destname=None):
1280@@ -112,7 +132,7 @@
1281 if destname is None:
1282 destname = os.path.basename(url)
1283 destpath = os.path.join(os.path.abspath('test-data'), destname)
1284- return urllib2.urlopen('file://' + destpath)
1285+ return urlopen('file://' + destpath)
1286
1287 def urlopen_file(self, filename):
1288 "Wrapper for urlopen_proxy for named files"
1289@@ -120,11 +140,11 @@
1290
1291 def urlopen_null(self, url):
1292 "urlopen for zero length files"
1293- return StringIO.StringIO('')
1294+ return BytesIO(b'')
1295
1296 def urlopen_404(self, url):
1297 "urlopen for errors"
1298- raise urllib2.HTTPError(url, 404, "Not Found", {}, None)
1299+ raise HTTPError(url, 404, "Not Found", {}, None)
1300
1301 def request_proxy(self, url, destname=None):
1302 "httplib2 proxy for grabbing the file from test-data"
1303@@ -132,7 +152,7 @@
1304 destname = os.path.basename(url)
1305 destpath = os.path.join(os.path.abspath('test-data'), destname)
1306 response = httplib2.Response({})
1307- with open(destpath, 'r') as f:
1308+ with open(destpath, 'rb') as f:
1309 body = f.read()
1310 return response, body
1311
1312@@ -141,10 +161,17 @@
1313 response = httplib2.Response({'status': 404})
1314 return response, "I'm a 404 Error"
1315
1316+ def request_404_then_proxy(self, url, destname=None):
1317+ "mock side_effect callable to chain request 404 & proxy"
1318+ if self.mock_http.called:
1319+ return self.request_proxy(url, destname)
1320+ return self.request_404(url)
1321+
1322 def test_local_copy(self):
1323 pkg = self.SourcePackage('example', '1.0-1', 'main',
1324 dscfile='test-data/example_1.0-1.dsc',
1325 workdir=self.workdir)
1326+ pkg.quiet = True
1327 pkg.pull()
1328 pkg.unpack()
1329
1330@@ -156,6 +183,7 @@
1331 pkg = self.SourcePackage(dscfile=os.path.join(self.workdir,
1332 'example_1.0-1.dsc'),
1333 workdir=self.workdir)
1334+ pkg.quiet = True
1335 pkg.pull()
1336 pkg.unpack()
1337
1338@@ -168,6 +196,7 @@
1339 dscfile=os.path.join(self.workdir,
1340 'example_1.0-1.dsc'),
1341 workdir=self.workdir)
1342+ pkg.quiet = True
1343 pkg.pull()
1344 pkg.unpack()
1345
1346@@ -177,31 +206,24 @@
1347 shutil.copy2('test-data/example_1.0-1.debian.tar.xz', self.workdir)
1348 with open(os.path.join(self.workdir, 'example_1.0-1.debian.tar.xz'),
1349 'r+b') as f:
1350- f.write('CORRUPTION')
1351+ f.write(b'CORRUPTION')
1352
1353 pkg = self.SourcePackage('example', '1.0-1', 'main',
1354 dscfile='test-data/example_1.0-1.dsc',
1355 workdir=self.workdir)
1356+ pkg.quiet = True
1357 pkg.pull()
1358
1359 def test_pull(self):
1360 dist = self.SourcePackage.distribution
1361 mirror = UDTConfig.defaults['%s_MIRROR' % dist.upper()]
1362 urlbase = '/pool/main/e/example/'
1363- httplib2.Http().AndReturn(self.mock_http)
1364- self.mock_http.request('https://launchpad.net/%s/+archive/primary/'
1365- '+files/example_1.0-1.dsc' % dist
1366- ).WithSideEffects(self.request_proxy)
1367- url_opener = self.mox.CreateMock(urllib2.OpenerDirector)
1368- url_opener.open(mirror + urlbase + 'example_1.0.orig.tar.gz'
1369- ).WithSideEffects(self.urlopen_proxy)
1370- url_opener.open(mirror + urlbase + 'example_1.0-1.debian.tar.xz'
1371- ).WithSideEffects(self.urlopen_proxy)
1372- self.mox.ReplayAll()
1373
1374 pkg = self.SourcePackage('example', '1.0-1', 'main',
1375 workdir=self.workdir)
1376- pkg.url_opener = url_opener
1377+
1378+ pkg.url_opener = self.url_opener
1379+ pkg.quiet = True
1380 pkg.pull()
1381
1382 def test_mirrors(self):
1383@@ -209,34 +231,24 @@
1384 mirror = 'http://mirror'
1385 lpbase = 'https://launchpad.net/ubuntu/+archive/primary/+files/'
1386 urlbase = '/pool/main/e/example/'
1387- httplib2.Http().AndReturn(self.mock_http)
1388- self.mock_http.request(lpbase + 'example_1.0-1.dsc'
1389- ).WithSideEffects(self.request_proxy)
1390- url_opener = self.mox.CreateMock(urllib2.OpenerDirector)
1391- url_opener.open(mirror + urlbase + 'example_1.0.orig.tar.gz'
1392- ).WithSideEffects(self.urlopen_null)
1393- url_opener.open(master + urlbase + 'example_1.0.orig.tar.gz'
1394- ).WithSideEffects(self.urlopen_404)
1395- url_opener.open(lpbase + 'example_1.0.orig.tar.gz'
1396- ).WithSideEffects(self.urlopen_proxy)
1397- url_opener.open(mirror + urlbase + 'example_1.0-1.debian.tar.xz'
1398- ).WithSideEffects(self.urlopen_proxy)
1399- self.mox.ReplayAll()
1400+ sequence = [self.urlopen_null, self.urlopen_404, self.urlopen_proxy,
1401+ self.urlopen_proxy]
1402+ def _callable_iter(*args, **kwargs):
1403+ return sequence.pop(0)(*args, **kwargs)
1404+ url_opener = mock.MagicMock(spec=OpenerDirector)
1405+ url_opener.open.side_effect = _callable_iter
1406
1407 pkg = self.SourcePackage('example', '1.0-1', 'main',
1408 workdir=self.workdir, mirrors=[mirror])
1409 pkg.url_opener = url_opener
1410+ pkg.quiet = True
1411 pkg.pull()
1412
1413 def test_dsc_missing(self):
1414- lpbase = 'https://launchpad.net/ubuntu/+archive/primary/+files/'
1415- httplib2.Http().AndReturn(self.mock_http)
1416- self.mock_http.request(lpbase + 'example_1.0-1.dsc'
1417- ).WithSideEffects(self.request_404)
1418- self.mox.ReplayAll()
1419-
1420+ self.mock_http.side_effect = self.request_404
1421 pkg = self.SourcePackage('example', '1.0-1', 'main',
1422 workdir=self.workdir)
1423+ pkg.quiet = True
1424 self.assertRaises(ubuntutools.archive.DownloadError, pkg.pull)
1425
1426
1427@@ -251,35 +263,24 @@
1428 lpbase = 'https://launchpad.net/debian/+archive/primary/+files/'
1429 base = '/pool/main/e/example/'
1430
1431- httplib2.Http().AndReturn(self.mock_http)
1432- self.mock_http.request(lpbase + 'example_1.0-1.dsc'
1433- ).WithSideEffects(self.request_proxy)
1434- url_opener = self.mox.CreateMock(urllib2.OpenerDirector)
1435- url_opener.open(debian_mirror + base + 'example_1.0.orig.tar.gz'
1436- ).WithSideEffects(self.urlopen_null)
1437- url_opener.open(debsec_mirror + base + 'example_1.0.orig.tar.gz'
1438- ).WithSideEffects(self.urlopen_404)
1439- url_opener.open(debian_master + base + 'example_1.0.orig.tar.gz'
1440- ).WithSideEffects(self.urlopen_404)
1441- url_opener.open(debsec_master + base + 'example_1.0.orig.tar.gz'
1442- ).WithSideEffects(self.urlopen_404)
1443- url_opener.open(lpbase + 'example_1.0.orig.tar.gz'
1444- ).WithSideEffects(self.urlopen_404)
1445- url_opener.open('http://snapshot.debian.org/mr/package/example/1.0-1/'
1446- 'srcfiles?fileinfo=1'
1447- ).WithSideEffects(lambda x: StringIO.StringIO(
1448- '{"fileinfo": {"hashabc": [{"name": "example_1.0.orig.tar.gz"}]}}'
1449- ))
1450- url_opener.open('http://snapshot.debian.org/file/hashabc'
1451- ).WithSideEffects(self.urlopen_file(
1452- 'example_1.0.orig.tar.gz'))
1453- url_opener.open(debian_mirror + base + 'example_1.0-1.debian.tar.xz'
1454- ).WithSideEffects(self.urlopen_proxy)
1455- self.mox.ReplayAll()
1456+ sequence = [self.urlopen_null,
1457+ self.urlopen_404,
1458+ self.urlopen_404,
1459+ self.urlopen_404,
1460+ self.urlopen_404,
1461+ lambda x: BytesIO(
1462+ b'{"fileinfo": {"hashabc": [{"name": "example_1.0.orig.tar.gz"}]}}'),
1463+ self.urlopen_file('example_1.0.orig.tar.gz'),
1464+ self.urlopen_proxy]
1465+ def _callable_iter(*args, **kwargs):
1466+ return sequence.pop(0)(*args, **kwargs)
1467+ url_opener = mock.MagicMock(spec=OpenerDirector)
1468+ url_opener.open.side_effect = _callable_iter
1469
1470 pkg = self.SourcePackage('example', '1.0-1', 'main',
1471 workdir=self.workdir, mirrors=[debian_mirror,
1472 debsec_mirror])
1473+ pkg.quiet = True
1474 pkg.url_opener = url_opener
1475 pkg.pull()
1476 pkg.unpack()
1477@@ -288,61 +289,35 @@
1478 mirror = 'http://mirror'
1479 lpbase = 'https://launchpad.net/debian/+archive/primary/+files/'
1480 base = '/pool/main/e/example/'
1481- httplib2.Http().AndReturn(self.mock_http)
1482- self.mock_http.request(lpbase + 'example_1.0-1.dsc'
1483- ).WithSideEffects(self.request_404)
1484- httplib2.Http().AndReturn(self.mock_http)
1485- self.mock_http.request(mirror + base + 'example_1.0-1.dsc'
1486- ).WithSideEffects(self.request_proxy)
1487- url_opener = self.mox.CreateMock(urllib2.OpenerDirector)
1488- url_opener.open(mirror + base + 'example_1.0.orig.tar.gz'
1489- ).WithSideEffects(self.urlopen_proxy)
1490- url_opener.open(mirror + base + 'example_1.0-1.debian.tar.xz'
1491- ).WithSideEffects(self.urlopen_proxy)
1492-
1493- def fake_gpg_info(self, message, keyrings=None):
1494- return debian.deb822.GpgInfo.from_output(
1495- '[GNUPG:] GOODSIG DEADBEEF Joe Developer '
1496- '<joe@example.net>')
1497- # We have to stub this out without mox because there some versions of
1498- # python-debian will pass keyrings=None, others won't.
1499- # http://code.google.com/p/pymox/issues/detail?id=37
1500- self.mox.stubs.Set(debian.deb822.GpgInfo, 'from_sequence',
1501- types.MethodType(fake_gpg_info,
1502- debian.deb822.GpgInfo,
1503- debian.deb822.GpgInfo))
1504-
1505- self.mox.ReplayAll()
1506+ self.mock_http.side_effect = self.request_404_then_proxy
1507+
1508+ patcher = mock.patch.object(debian.deb822.GpgInfo, 'from_sequence')
1509+ self.addCleanup(patcher.stop)
1510+ mock_gpg_info = patcher.start()
1511+ mock_gpg_info.return_value = debian.deb822.GpgInfo.from_output(
1512+ '[GNUPG:] GOODSIG DEADBEEF Joe Developer '
1513+ '<joe@example.net>')
1514
1515 pkg = self.SourcePackage('example', '1.0-1', 'main',
1516 workdir=self.workdir, mirrors=[mirror])
1517- pkg.url_opener = url_opener
1518+ pkg.url_opener = self.url_opener
1519 pkg.pull()
1520
1521 def test_dsc_badsig(self):
1522 mirror = 'http://mirror'
1523 lpbase = 'https://launchpad.net/debian/+archive/primary/+files/'
1524 base = '/pool/main/e/example/'
1525- httplib2.Http().AndReturn(self.mock_http)
1526- self.mock_http.request(lpbase + 'example_1.0-1.dsc'
1527- ).WithSideEffects(self.request_404)
1528- httplib2.Http().AndReturn(self.mock_http)
1529- self.mock_http.request(mirror + base + 'example_1.0-1.dsc'
1530- ).WithSideEffects(self.request_proxy)
1531-
1532- def fake_gpg_info(self, message, keyrings=None):
1533- return debian.deb822.GpgInfo.from_output(
1534- '[GNUPG:] ERRSIG DEADBEEF')
1535- # We have to stub this out without mox because there some versions of
1536- # python-debian will pass keyrings=None, others won't.
1537- # http://code.google.com/p/pymox/issues/detail?id=37
1538- self.mox.stubs.Set(debian.deb822.GpgInfo, 'from_sequence',
1539- types.MethodType(fake_gpg_info,
1540- debian.deb822.GpgInfo,
1541- debian.deb822.GpgInfo))
1542-
1543- self.mox.ReplayAll()
1544-
1545+ self.mock_http.side_effect = self.request_404_then_proxy
1546+
1547+ patcher = mock.patch.object(debian.deb822.GpgInfo, 'from_sequence')
1548+ self.addCleanup(patcher.stop)
1549+ mock_gpg_info = patcher.start()
1550+ mock_gpg_info.return_value = debian.deb822.GpgInfo.from_output(
1551+ '[GNUPG:] ERRSIG DEADBEEF')
1552+
1553 pkg = self.SourcePackage('example', '1.0-1', 'main',
1554 workdir=self.workdir, mirrors=[mirror])
1555- self.assertRaises(ubuntutools.archive.DownloadError, pkg.pull)
1556+ try:
1557+ self.assertRaises(ubuntutools.archive.DownloadError, pkg.pull)
1558+ except URLError:
1559+ raise unittest.SkipTest('Test needs addr resolution to work')
1560
1561=== modified file 'ubuntutools/test/test_config.py'
1562--- ubuntutools/test/test_config.py 2013-05-15 00:18:50 +0000
1563+++ ubuntutools/test/test_config.py 2014-12-19 22:54:27 +0000
1564@@ -15,19 +15,25 @@
1565 # OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
1566 # PERFORMANCE OF THIS SOFTWARE.
1567
1568-import __builtin__
1569+try:
1570+ import builtins
1571+except ImportError:
1572+ import __builtin__
1573 import os
1574 import sys
1575 import locale
1576-from StringIO import StringIO
1577+try:
1578+ from StringIO import StringIO
1579+except:
1580+ from io import StringIO
1581
1582-import mox
1583+import mock
1584
1585 from ubuntutools.config import UDTConfig, ubu_email
1586 from ubuntutools.logger import Logger
1587 from ubuntutools.test import unittest
1588
1589-class ConfigTestCase(mox.MoxTestBase, unittest.TestCase):
1590+class ConfigTestCase(unittest.TestCase):
1591 _config_files = {
1592 'system': '',
1593 'user': '',
1594@@ -46,7 +52,18 @@
1595
1596 def setUp(self):
1597 super(ConfigTestCase, self).setUp()
1598- self.mox.stubs.Set(__builtin__, 'open', self._fake_open)
1599+ if sys.version_info[0] < 3:
1600+ self.assertRegex = self.assertRegexpMatches
1601+ m = mock.mock_open()
1602+ m.side_effect = self._fake_open
1603+ if sys.version_info[0] >= 3:
1604+ target = 'builtins.open'
1605+ else:
1606+ target = '__builtin__.open'
1607+ patcher = mock.patch(target, m)
1608+ self.addCleanup(patcher.stop)
1609+ patcher.start()
1610+
1611 Logger.stdout = StringIO()
1612 Logger.stderr = StringIO()
1613
1614@@ -63,7 +80,7 @@
1615 def clean_environment(self):
1616 self._config_files['system'] = ''
1617 self._config_files['user'] = ''
1618- for k in os.environ.keys():
1619+ for k in list(os.environ.keys()):
1620 if k.startswith(('UBUNTUTOOLS_', 'TEST_')):
1621 del os.environ[k]
1622
1623@@ -97,8 +114,8 @@
1624 errs = Logger.stderr.getvalue().strip()
1625 Logger.stderr = StringIO()
1626 self.assertEqual(len(errs.splitlines()), 1)
1627- self.assertRegexpMatches(errs,
1628- r'Warning: Cannot parse.*\bCOMMAND_EXECUTION=a')
1629+ self.assertRegex(errs,
1630+ r'Warning: Cannot parse.*\bCOMMAND_EXECUTION=a')
1631
1632 def get_value(self, *args, **kwargs):
1633 config = UDTConfig(prefix='TEST')
1634@@ -137,8 +154,8 @@
1635 errs = Logger.stderr.getvalue().strip()
1636 Logger.stderr = StringIO()
1637 self.assertEqual(len(errs.splitlines()), 1)
1638- self.assertRegexpMatches(errs,
1639- r'deprecated.*\bCOMPATFOOBAR\b.*\bTEST_QUX\b')
1640+ self.assertRegex(errs,
1641+ r'deprecated.*\bCOMPATFOOBAR\b.*\bTEST_QUX\b')
1642
1643 def test_boolean(self):
1644 self._config_files['user'] = "TEST_BOOLEAN=yes"
1645@@ -150,7 +167,7 @@
1646
1647 def test_nonpackagewide(self):
1648 self._config_files['user'] = 'UBUNTUTOOLS_FOOBAR=a'
1649- self.assertEquals(self.get_value('FOOBAR'), None)
1650+ self.assertEqual(self.get_value('FOOBAR'), None)
1651
1652
1653 class UbuEmailTestCase(unittest.TestCase):
1654@@ -217,7 +234,11 @@
1655 encoding = locale.getdefaultlocale()[1]
1656 if not encoding:
1657 encoding = 'utf-8'
1658- name = 'Jöe Déveloper'.decode('utf-8')
1659- os.environ['DEBFULLNAME'] = name.encode(encoding)
1660+ name = 'Jöe Déveloper'
1661+ env_name = name
1662+ if isinstance(name, bytes):
1663+ name = 'Jöe Déveloper'.decode('utf-8')
1664+ env_name = name.encode(encoding)
1665+ os.environ['DEBFULLNAME'] = env_name
1666 os.environ['DEBEMAIL'] = email = 'joe@example.net'
1667 self.assertEqual(ubu_email(), (name, email))
1668
1669=== modified file 'ubuntutools/test/test_help.py'
1670--- ubuntutools/test/test_help.py 2012-05-06 04:06:41 +0000
1671+++ ubuntutools/test/test_help.py 2014-12-19 22:54:27 +0000
1672@@ -45,6 +45,7 @@
1673 null = open('/dev/null', 'r')
1674 process = subprocess.Popen(['./' + script, '--help'],
1675 close_fds=True, stdin=null,
1676+ universal_newlines=True,
1677 stdout=subprocess.PIPE,
1678 stderr=subprocess.PIPE)
1679 started = time.time()
1680@@ -57,7 +58,10 @@
1681
1682 while time.time() - started < TIMEOUT:
1683 for fd in select.select(fds, [], fds, TIMEOUT)[0]:
1684- out.append(os.read(fd, 1024))
1685+ output = os.read(fd, 1024)
1686+ if not isinstance(output, str):
1687+ output = output.decode('utf-8')
1688+ out.append(output)
1689 if process.poll() is not None:
1690 break
1691
1692
1693=== modified file 'ubuntutools/test/test_logger.py'
1694--- ubuntutools/test/test_logger.py 2013-03-18 23:18:02 +0000
1695+++ ubuntutools/test/test_logger.py 2014-12-19 22:54:27 +0000
1696@@ -14,7 +14,10 @@
1697 # OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
1698 # PERFORMANCE OF THIS SOFTWARE.
1699
1700-import StringIO
1701+try:
1702+ from StringIO import StringIO
1703+except:
1704+ from io import StringIO
1705 import sys
1706
1707 from ubuntutools.logger import Logger
1708@@ -23,8 +26,8 @@
1709
1710 class LoggerTestCase(unittest.TestCase):
1711 def setUp(self):
1712- Logger.stdout = StringIO.StringIO()
1713- Logger.stderr = StringIO.StringIO()
1714+ Logger.stdout = StringIO()
1715+ Logger.stderr = StringIO()
1716 self._script_name = Logger.script_name
1717 Logger.script_name = 'test'
1718 self._verbose = Logger.verbose
1719
1720=== modified file 'ubuntutools/test/test_pylint.py'
1721--- ubuntutools/test/test_pylint.py 2011-06-24 14:32:07 +0000
1722+++ ubuntutools/test/test_pylint.py 2014-12-19 22:54:27 +0000
1723@@ -25,9 +25,6 @@
1724 r"No name '\w+Error' in module 'launchpadlib\.errors'",
1725 # http://www.logilab.org/ticket/51250:
1726 r"Module 'hashlib' has no '(md5|sha(1|224|256|384|512))' member",
1727- # mox:
1728- r"Instance of '.+' has no '(WithSideEffects|MultipleTimes|AndReturn)' "
1729- r"member",
1730 # pylint doesn't like *args/**kwargs
1731 r"Instance of 'Popen' has no '.*' member",
1732 )]
1733
1734=== modified file 'ubuntutools/test/test_update_maintainer.py'
1735--- ubuntutools/test/test_update_maintainer.py 2013-03-18 23:18:02 +0000
1736+++ ubuntutools/test/test_update_maintainer.py 2014-12-19 22:54:27 +0000
1737@@ -16,12 +16,19 @@
1738
1739 """Test suite for ubuntutools.update_maintainer"""
1740
1741-import __builtin__
1742+try:
1743+ import builtins
1744+except ImportError:
1745+ import __builtin__
1746+try:
1747+ from StringIO import StringIO
1748+except:
1749+ from io import StringIO
1750+
1751 import os
1752-import StringIO
1753 import sys
1754
1755-import mox
1756+import mock
1757
1758 from ubuntutools.logger import Logger
1759 from ubuntutools.test import unittest
1760@@ -186,7 +193,7 @@
1761 """
1762
1763 #pylint: disable=R0904
1764-class UpdateMaintainerTestCase(mox.MoxTestBase, unittest.TestCase):
1765+class UpdateMaintainerTestCase(unittest.TestCase):
1766 """TestCase object for ubuntutools.update_maintainer"""
1767
1768 _directory = "/"
1769@@ -210,18 +217,30 @@
1770 (mode == "r" and self._files[base] is None)):
1771 raise IOError("No such file or directory: '%s'" % filename)
1772 if mode == "w":
1773- self._files[base] = StringIO.StringIO()
1774+ self._files[base] = StringIO()
1775 self._files[base].close = lambda: None
1776 return self._files[base]
1777
1778 #pylint: disable=C0103
1779 def setUp(self):
1780- super(UpdateMaintainerTestCase, self).setUp()
1781- self.mox.stubs.Set(__builtin__, 'open', self._fake_open)
1782- self.mox.stubs.Set(os.path, 'isfile', self._fake_isfile)
1783- self._files["rules"] = StringIO.StringIO(_SIMPLE_RULES)
1784- Logger.stdout = StringIO.StringIO()
1785- Logger.stderr = StringIO.StringIO()
1786+ if sys.version_info[0] < 3:
1787+ self.assertRegex = self.assertRegexpMatches
1788+ m = mock.mock_open()
1789+ m.side_effect = self._fake_open
1790+ if sys.version_info[0] >= 3:
1791+ target = 'builtins.open'
1792+ else:
1793+ target = '__builtin__.open'
1794+ patcher = mock.patch(target, m)
1795+ self.addCleanup(patcher.stop)
1796+ patcher.start()
1797+ m = mock.MagicMock(side_effect=self._fake_isfile)
1798+ patcher = mock.patch('os.path.isfile', m)
1799+ self.addCleanup(patcher.stop)
1800+ patcher.start()
1801+ self._files["rules"] = StringIO(_SIMPLE_RULES)
1802+ Logger.stdout = StringIO()
1803+ Logger.stderr = StringIO()
1804
1805 def tearDown(self):
1806 self.assertEqual(Logger.stdout.getvalue(), '')
1807@@ -236,8 +255,8 @@
1808 #pylint: enable=C0103
1809 def test_debian_package(self):
1810 """Test: Don't update Maintainer field if target is Debian."""
1811- self._files["changelog"] = StringIO.StringIO(_UNSTABLE_CHANGELOG)
1812- self._files["control"] = StringIO.StringIO(_ABP_CONTROL)
1813+ self._files["changelog"] = StringIO(_UNSTABLE_CHANGELOG)
1814+ self._files["control"] = StringIO(_ABP_CONTROL)
1815 update_maintainer(self._directory)
1816 self.assertEqual(self._files["control"].getvalue(), _ABP_CONTROL)
1817
1818@@ -246,52 +265,52 @@
1819
1820 The Maintainer field needs to be update even if
1821 XSBC-Original-Maintainer has an @ubuntu.com address."""
1822- self._files["changelog"] = StringIO.StringIO(_LUCID_CHANGELOG)
1823- self._files["control"] = StringIO.StringIO(_AXIS2C_CONTROL)
1824+ self._files["changelog"] = StringIO(_LUCID_CHANGELOG)
1825+ self._files["control"] = StringIO(_AXIS2C_CONTROL)
1826 update_maintainer(self._directory)
1827 self.assertEqual(self._files["control"].getvalue(), _AXIS2C_UPDATED)
1828 warnings = Logger.stderr.getvalue().strip()
1829- Logger.stderr = StringIO.StringIO()
1830+ Logger.stderr = StringIO()
1831 self.assertEqual(len(warnings.splitlines()), 1)
1832- self.assertRegexpMatches(warnings, "Warning: Overwriting original "
1833+ self.assertRegex(warnings, "Warning: Overwriting original "
1834 "maintainer: Soren Hansen "
1835 "<soren@ubuntu.com>")
1836
1837 def test_update_maintainer(self):
1838 """Test: Update Maintainer field."""
1839- self._files["changelog"] = StringIO.StringIO(_LUCID_CHANGELOG)
1840- self._files["control"] = StringIO.StringIO(_ABP_CONTROL)
1841+ self._files["changelog"] = StringIO(_LUCID_CHANGELOG)
1842+ self._files["control"] = StringIO(_ABP_CONTROL)
1843 update_maintainer(self._directory)
1844 self.assertEqual(self._files["control"].getvalue(), _ABP_UPDATED)
1845
1846 def test_update_old_maintainer(self):
1847 """Test: Update old MOTU address."""
1848- self._files["changelog"] = StringIO.StringIO(_UNSTABLE_CHANGELOG)
1849- self._files["control.in"] = StringIO.StringIO(_ABP_OLD_MAINTAINER)
1850+ self._files["changelog"] = StringIO(_UNSTABLE_CHANGELOG)
1851+ self._files["control.in"] = StringIO(_ABP_OLD_MAINTAINER)
1852 update_maintainer(self._directory, True)
1853 self.assertEqual(self._files["control.in"].getvalue(), _ABP_UPDATED)
1854
1855 def test_comments_in_control(self):
1856 """Test: Update Maintainer field in a control file containing
1857 comments."""
1858- self._files["changelog"] = StringIO.StringIO(_LUCID_CHANGELOG)
1859- self._files["control"] = StringIO.StringIO(_SEAHORSE_PLUGINS_CONTROL)
1860+ self._files["changelog"] = StringIO(_LUCID_CHANGELOG)
1861+ self._files["control"] = StringIO(_SEAHORSE_PLUGINS_CONTROL)
1862 update_maintainer(self._directory)
1863 self.assertEqual(self._files["control"].getvalue(),
1864 _SEAHORSE_PLUGINS_UPDATED)
1865
1866 def test_skip_smart_rules(self):
1867 """Test: Skip update when XSBC-Original in debian/rules."""
1868- self._files["changelog"] = StringIO.StringIO(_LUCID_CHANGELOG)
1869- self._files["control"] = StringIO.StringIO(_ABP_CONTROL)
1870- self._files["rules"] = StringIO.StringIO(_COMPLEX_RULES)
1871+ self._files["changelog"] = StringIO(_LUCID_CHANGELOG)
1872+ self._files["control"] = StringIO(_ABP_CONTROL)
1873+ self._files["rules"] = StringIO(_COMPLEX_RULES)
1874 update_maintainer(self._directory)
1875 self.assertEqual(self._files["control"].getvalue(), _ABP_CONTROL)
1876
1877 def test_missing_rules(self):
1878 """Test: Skip XSBC-Original test when debian/rules is missing."""
1879- self._files["changelog"] = StringIO.StringIO(_LUCID_CHANGELOG)
1880- self._files["control"] = StringIO.StringIO(_ABP_CONTROL)
1881+ self._files["changelog"] = StringIO(_LUCID_CHANGELOG)
1882+ self._files["control"] = StringIO(_ABP_CONTROL)
1883 self._files["rules"] = None
1884 update_maintainer(self._directory)
1885 self.assertEqual(self._files["control"].getvalue(), _ABP_UPDATED)
1886
1887=== modified file 'ubuntutools/update_maintainer.py'
1888--- ubuntutools/update_maintainer.py 2013-03-18 23:18:02 +0000
1889+++ ubuntutools/update_maintainer.py 2014-12-19 22:54:27 +0000
1890@@ -14,6 +14,8 @@
1891 # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1892 # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1893
1894+from __future__ import print_function
1895+
1896 """This module is for updating the Maintainer field of an Ubuntu package."""
1897
1898 import os
1899@@ -125,7 +127,7 @@
1900 if os.path.isfile(rules_file) and \
1901 'XSBC-Original-' in open(rules_file).read():
1902 if verbose:
1903- print "XSBC-Original is managed by 'rules' file. Doing nothing."
1904+ print("XSBC-Original is managed by 'rules' file. Doing nothing.")
1905 control_files = []
1906
1907 return (changelog_file, control_files)
1908@@ -144,7 +146,7 @@
1909 """
1910 try:
1911 changelog_file, control_files = _find_files(debian_directory, verbose)
1912- except MaintainerUpdateException, e:
1913+ except MaintainerUpdateException as e:
1914 Logger.error(str(e))
1915 raise
1916
1917@@ -159,8 +161,8 @@
1918
1919 if original_maintainer.strip().lower() in _PREVIOUS_UBUNTU_MAINTAINER:
1920 if verbose:
1921- print "The old maintainer was: %s" % original_maintainer
1922- print "Resetting as: %s" % _UBUNTU_MAINTAINER
1923+ print("The old maintainer was: %s" % original_maintainer)
1924+ print("Resetting as: %s" % _UBUNTU_MAINTAINER)
1925 control.set_maintainer(_UBUNTU_MAINTAINER)
1926 control.save()
1927 continue
1928@@ -173,7 +175,7 @@
1929
1930 if distribution in ("stable", "testing", "unstable", "experimental"):
1931 if verbose:
1932- print "The package targets Debian. Doing nothing."
1933+ print("The package targets Debian. Doing nothing.")
1934 return
1935
1936 if control.get_original_maintainer() is not None:
1937@@ -181,8 +183,8 @@
1938 control.get_original_maintainer())
1939
1940 if verbose:
1941- print "The original maintainer is: %s" % original_maintainer
1942- print "Resetting as: %s" % _UBUNTU_MAINTAINER
1943+ print("The original maintainer is: %s" % original_maintainer)
1944+ print("Resetting as: %s" % _UBUNTU_MAINTAINER)
1945 control.set_original_maintainer(original_maintainer)
1946 control.set_maintainer(_UBUNTU_MAINTAINER)
1947 control.save()
1948@@ -194,7 +196,7 @@
1949 """Restore the original maintainer"""
1950 try:
1951 changelog_file, control_files = _find_files(debian_directory, verbose)
1952- except MaintainerUpdateException, e:
1953+ except MaintainerUpdateException as e:
1954 Logger.error(str(e))
1955 raise
1956
1957@@ -204,7 +206,7 @@
1958 if not orig_maintainer:
1959 continue
1960 if verbose:
1961- print "Restoring original maintainer: %s" % orig_maintainer
1962+ print("Restoring original maintainer: %s" % orig_maintainer)
1963 control.set_maintainer(orig_maintainer)
1964 control.remove_original_maintainer()
1965 control.save()