Merge ~ahasenack/ubuntu/+source/python-tempita:jammy-tempita-2to3-ftbfs into ubuntu/+source/python-tempita:ubuntu/devel

Proposed by Andreas Hasenack
Status: Merged
Merged at revision: 52402bfdb4e153575314854771c66c3a3fac548f
Proposed branch: ~ahasenack/ubuntu/+source/python-tempita:jammy-tempita-2to3-ftbfs
Merge into: ubuntu/+source/python-tempita:ubuntu/devel
Diff against target: 365 lines (+319/-1)
6 files modified
debian/changelog (+11/-0)
debian/control (+2/-1)
debian/patches/2to3.patch (+236/-0)
debian/patches/fix-doc-tests.patch (+65/-0)
debian/patches/series (+2/-0)
debian/rules (+3/-0)
Reviewer Review Type Date Requested Status
Athos Ribeiro (community) Approve
Canonical Server Core Reviewers Pending
Review via email: mp+417740@code.launchpad.net

Description of the change

This fixes two issues:

- ftbfs
python 3.10 removed[1][2] the 2to3 option in setup.py, which would run 2to3 (you guessed it) against the code during build, and install the result.
My "fix" here was to build this module with python 3.9 on impish, where the 2to3 option still worked, and diff the result. That's the patch I'm applying on jammy.

- doctests
For some reason I didn't investigate too deeply, the doctests were not being run[3]. When the nose runner was called, the directory it was given was empty, so nothing was run. I overrode the test target in d/rules and called them directly. Which led me to another fix to get them to pass: the exception raised was namespaced to the module (tempita.<exception>), instead of just the exception name. I don't know if this is a python3 change, or what. Again, the simplest change was to just adjust the docstring, and the tests pass correctly.

A bileto ticket with a PPA is available[4], and the dep8 tests actually ran. Failures are just dependencies in i386 and I didn't investigate those :/
While this is up for review, I might still check that.

1. https://bugs.python.org/issue40360
2. https://github.com/pypa/setuptools/issues/2086
3. https://bugs.launchpad.net/bugs/1966532
4. https://bileto.ubuntu.com/#/ticket/4819

To post a comment you must log in.
Revision history for this message
Athos Ribeiro (athos-ribeiro) wrote :

I will take a look at this one :)

Revision history for this message
Athos Ribeiro (athos-ribeiro) wrote :

LGTM; thanks, Andreas!

review: Approve
Revision history for this message
Andreas Hasenack (ahasenack) wrote :

Thanks Athos, uploaded:

$ dput ubuntu ../python-tempita_0.5.2-6ubuntu1_source.changes
D: Setting host argument.
Checking signature on .changes
gpg: ../python-tempita_0.5.2-6ubuntu1_source.changes: Valid signature from AC983EB5BF6BCBA9
Checking signature on .dsc
gpg: ../python-tempita_0.5.2-6ubuntu1.dsc: Valid signature from AC983EB5BF6BCBA9
Uploading to ubuntu (via ftp to upload.ubuntu.com):
  Uploading python-tempita_0.5.2-6ubuntu1.dsc: done.
  Uploading python-tempita_0.5.2-6ubuntu1.debian.tar.xz: done.
  Uploading python-tempita_0.5.2-6ubuntu1_source.buildinfo: done.
  Uploading python-tempita_0.5.2-6ubuntu1_source.changes: done.
Successfully uploaded packages.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/debian/changelog b/debian/changelog
2index 723fab7..62a734a 100644
3--- a/debian/changelog
4+++ b/debian/changelog
5@@ -1,3 +1,14 @@
6+python-tempita (0.5.2-6ubuntu1) jammy; urgency=medium
7+
8+ * d/p/2to3.patch: quick python 3 conversion using 2to3, exactly how it
9+ was being done automatically during build until python 3.10's
10+ setuptools removed the 2to3 option from setup.py (LP: #1966323)
11+ * Fix and run the existing doc tests (LP: #1966532):
12+ - d/p/fix-doc-tests.patch: fix exception name raised in doctests
13+ - d/rules: run doc tests
14+
15+ -- Andreas Hasenack <andreas@canonical.com> Fri, 25 Mar 2022 22:12:13 -0300
16+
17 python-tempita (0.5.2-6) unstable; urgency=medium
18
19 [ Ondřej Nový ]
20diff --git a/debian/control b/debian/control
21index f6c39ec..4a7b14e 100644
22--- a/debian/control
23+++ b/debian/control
24@@ -1,7 +1,8 @@
25 Source: python-tempita
26 Section: python
27 Priority: optional
28-Maintainer: Debian Python Modules Team <python-modules-team@lists.alioth.debian.org>
29+Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>
30+XSBC-Original-Maintainer: Debian Python Modules Team <python-modules-team@lists.alioth.debian.org>
31 Uploaders:
32 Ondřej Kobližek <koblizeko@gmail.com>,
33 Build-Depends:
34diff --git a/debian/patches/2to3.patch b/debian/patches/2to3.patch
35new file mode 100644
36index 0000000..9d4045c
37--- /dev/null
38+++ b/debian/patches/2to3.patch
39@@ -0,0 +1,236 @@
40+Description: quick and dirty conversion to python3
41+ Tempita was relying on setuptools' 2to3 option which would run 2to3
42+ automatically on the tree at build time, prior to installation. That option is
43+ gone in python 3.10.
44+ .
45+ What I did here was build it with python 3.9 and diff the installed python
46+ files against the upstream source, and this is the result.
47+ .
48+ This is probably not the best conversion to python 3, but it's what was being
49+ done up until now anyway, automatically, during build.
50+Author: Andreas Hasenack <andreas@canonical.com>
51+Forwarded: no, upstream seems to have ceased development
52+Bug-Ubuntu: https://bugs.launchpad.net/bugs/1966323
53+Bug-Debian: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=997633
54+Last-Update: 2022-03-25
55+
56+--- a/tempita/__init__.py
57++++ b/tempita/__init__.py
58+@@ -32,10 +32,10 @@
59+ import re
60+ import sys
61+ import cgi
62+-from urllib import quote as url_quote
63++from urllib.parse import quote as url_quote
64+ import os
65+ import tokenize
66+-from cStringIO import StringIO
67++from io import StringIO
68+ from tempita._looper import looper
69+ from tempita.compat3 import PY3, bytes, basestring_, next, is_unicode, coerce_text
70+
71+@@ -101,7 +101,7 @@
72+ delimiters = (self.default_namespace['start_braces'],
73+ self.default_namespace['end_braces'])
74+ else:
75+- assert len(delimiters) == 2 and all([isinstance(delimeter, basestring)
76++ assert len(delimiters) == 2 and all([isinstance(delimeter, str)
77+ for delimeter in delimiters])
78+ self.default_namespace = self.__class__.default_namespace.copy()
79+ self.default_namespace['start_braces'] = delimiters[0]
80+@@ -198,7 +198,7 @@
81+ position=None, name=self.name)
82+ templ = self.get_template(inherit_template, self)
83+ self_ = TemplateObject(self.name)
84+- for name, value in defs.iteritems():
85++ for name, value in defs.items():
86+ setattr(self_, name, value)
87+ self_.body = body
88+ ns = ns.copy()
89+@@ -294,7 +294,7 @@
90+ try:
91+ try:
92+ value = eval(code, self.default_namespace, ns)
93+- except SyntaxError, e:
94++ except SyntaxError as e:
95+ raise SyntaxError(
96+ 'invalid syntax in expression: %s' % code)
97+ return value
98+@@ -306,12 +306,12 @@
99+ else:
100+ arg0 = coerce_text(e)
101+ e.args = (self._add_line_info(arg0, pos),)
102+- raise exc_info[0], e, exc_info[2]
103++ raise exc_info[0](e).with_traceback(exc_info[2])
104+
105+ def _exec(self, code, ns, pos):
106+ __traceback_hide__ = True
107+ try:
108+- exec code in self.default_namespace, ns
109++ exec(code, self.default_namespace, ns)
110+ except:
111+ exc_info = sys.exc_info()
112+ e = exc_info[1]
113+@@ -319,7 +319,7 @@
114+ e.args = (self._add_line_info(e.args[0], pos),)
115+ else:
116+ e.args = (self._add_line_info(None, pos),)
117+- raise exc_info[0], e, exc_info[2]
118++ raise exc_info[0](e).with_traceback(exc_info[2])
119+
120+ def _repr(self, value, pos):
121+ __traceback_hide__ = True
122+@@ -328,7 +328,7 @@
123+ return ''
124+ if self._unicode:
125+ try:
126+- value = unicode(value)
127++ value = str(value)
128+ except UnicodeDecodeError:
129+ value = bytes(value)
130+ else:
131+@@ -341,7 +341,7 @@
132+ exc_info = sys.exc_info()
133+ e = exc_info[1]
134+ e.args = (self._add_line_info(e.args[0], pos),)
135+- raise exc_info[0], e, exc_info[2]
136++ raise exc_info[0](e).with_traceback(exc_info[2])
137+ else:
138+ if self._unicode and isinstance(value, bytes):
139+ if not self.default_encoding:
140+@@ -350,7 +350,7 @@
141+ '(no default_encoding provided)' % value)
142+ try:
143+ value = value.decode(self.default_encoding)
144+- except UnicodeDecodeError, e:
145++ except UnicodeDecodeError as e:
146+ raise UnicodeDecodeError(
147+ e.encoding,
148+ e.object,
149+@@ -387,7 +387,7 @@
150+ class bunch(dict):
151+
152+ def __init__(self, **kw):
153+- for name, value in kw.iteritems():
154++ for name, value in kw.items():
155+ setattr(self, name, value)
156+
157+ def __setattr__(self, name, value):
158+@@ -410,7 +410,7 @@
159+
160+ def __repr__(self):
161+ items = [
162+- (k, v) for k, v in self.iteritems()]
163++ (k, v) for k, v in self.items()]
164+ items.sort()
165+ return '<%s %s>' % (
166+ self.__class__.__name__,
167+@@ -463,7 +463,7 @@
168+
169+
170+ def attr(**kw):
171+- kw = list(kw.iteritems())
172++ kw = list(kw.items())
173+ kw.sort()
174+ parts = []
175+ for name, value in kw:
176+@@ -545,7 +545,7 @@
177+ values = {}
178+ sig_args, var_args, var_kw, defaults = self._func_signature
179+ extra_kw = {}
180+- for name, value in kw.iteritems():
181++ for name, value in kw.items():
182+ if not var_kw and name not in sig_args:
183+ raise TypeError(
184+ 'Unexpected argument %s' % name)
185+@@ -568,7 +568,7 @@
186+ raise TypeError(
187+ 'Extra position arguments: %s'
188+ % ', '.join(repr(v) for v in args))
189+- for name, value_expr in defaults.iteritems():
190++ for name, value_expr in defaults.items():
191+ if name not in values:
192+ values[name] = self._template._eval(
193+ value_expr, self._ns, self._pos)
194+@@ -614,7 +614,7 @@
195+ return 'Empty'
196+
197+ def __unicode__(self):
198+- return u''
199++ return ''
200+
201+ def __iter__(self):
202+ return iter(())
203+@@ -1158,7 +1158,7 @@
204+ vars.update(os.environ)
205+ for value in args:
206+ if '=' not in value:
207+- print('Bad argument: %r' % value)
208++ print(('Bad argument: %r' % value))
209+ sys.exit(2)
210+ name, value = value.split('=', 1)
211+ if name.startswith('py:'):
212+--- a/tempita/_looper.py
213++++ b/tempita/_looper.py
214+@@ -7,9 +7,9 @@
215+ looper you can get a better sense of the context. Use like::
216+
217+ >>> for loop, item in looper(['a', 'b', 'c']):
218+- ... print loop.number, item
219++ ... print(loop.number, item)
220+ ... if not loop.last:
221+- ... print '---'
222++ ... print('---')
223+ 1 a
224+ ---
225+ 2 b
226+@@ -161,3 +161,4 @@
227+ return getter(item) != getter(other)
228+ else:
229+ return item[getter] != other[getter]
230++
231+--- a/tempita/compat3.py
232++++ b/tempita/compat3.py
233+@@ -6,7 +6,7 @@
234+
235+ if sys.version < "3":
236+ b = bytes = str
237+- basestring_ = basestring
238++ basestring_ = str
239+ else:
240+
241+ def b(s):
242+@@ -20,14 +20,14 @@
243+ if sys.version < "3":
244+
245+ def next(obj):
246+- return obj.next()
247++ return obj.__next__()
248+ else:
249+ next = next
250+
251+ if sys.version < "3":
252+
253+ def is_unicode(obj):
254+- return isinstance(obj, unicode)
255++ return isinstance(obj, str)
256+ else:
257+
258+ def is_unicode(obj):
259+@@ -41,7 +41,7 @@
260+ else:
261+ attr = '__str__'
262+ if hasattr(v, attr):
263+- return unicode(v)
264++ return str(v)
265+ else:
266+ return bytes(v)
267+ return v
268+--- a/setup.py
269++++ b/setup.py
270+@@ -36,5 +36,4 @@
271+ test_suite='nose.collector',
272+ include_package_data=True,
273+ zip_safe=True,
274+- use_2to3=True,
275+ )
276diff --git a/debian/patches/fix-doc-tests.patch b/debian/patches/fix-doc-tests.patch
277new file mode 100644
278index 0000000..6354ff2
279--- /dev/null
280+++ b/debian/patches/fix-doc-tests.patch
281@@ -0,0 +1,65 @@
282+Description: fix doctests
283+Author: Andreas Hasenack <andreas@canonical.com>
284+Bug-Ubuntu: https://bugs.launchpad.net/ubuntu/+source/python-tempita/+bug/1966532
285+Forwarded: no, upstream seems to have ceased development
286+Last-Update: 2022-03-25
287+--- a/tempita/__init__.py
288++++ b/tempita/__init__.py
289+@@ -644,15 +644,15 @@
290+ >>> lex('hey {{')
291+ Traceback (most recent call last):
292+ ...
293+- TemplateError: No }} to finish last expression at line 1 column 7
294++ tempita.TemplateError: No }} to finish last expression at line 1 column 7
295+ >>> lex('hey }}')
296+ Traceback (most recent call last):
297+ ...
298+- TemplateError: }} outside expression at line 1 column 7
299++ tempita.TemplateError: }} outside expression at line 1 column 7
300+ >>> lex('hey {{ {{')
301+ Traceback (most recent call last):
302+ ...
303+- TemplateError: {{ inside expression at line 1 column 10
304++ tempita.TemplateError: {{ inside expression at line 1 column 10
305+
306+ """
307+ if delimiters is None:
308+@@ -790,31 +790,31 @@
309+ >>> parse('{{continue}}')
310+ Traceback (most recent call last):
311+ ...
312+- TemplateError: continue outside of for loop at line 1 column 3
313++ tempita.TemplateError: continue outside of for loop at line 1 column 3
314+ >>> parse('{{if x}}foo')
315+ Traceback (most recent call last):
316+ ...
317+- TemplateError: No {{endif}} at line 1 column 3
318++ tempita.TemplateError: No {{endif}} at line 1 column 3
319+ >>> parse('{{else}}')
320+ Traceback (most recent call last):
321+ ...
322+- TemplateError: else outside of an if block at line 1 column 3
323++ tempita.TemplateError: else outside of an if block at line 1 column 3
324+ >>> parse('{{if x}}{{for x in y}}{{endif}}{{endfor}}')
325+ Traceback (most recent call last):
326+ ...
327+- TemplateError: Unexpected endif at line 1 column 25
328++ tempita.TemplateError: Unexpected endif at line 1 column 25
329+ >>> parse('{{if}}{{endif}}')
330+ Traceback (most recent call last):
331+ ...
332+- TemplateError: if with no expression at line 1 column 3
333++ tempita.TemplateError: if with no expression at line 1 column 3
334+ >>> parse('{{for x y}}{{endfor}}')
335+ Traceback (most recent call last):
336+ ...
337+- TemplateError: Bad for (no "in") in 'x y' at line 1 column 3
338++ tempita.TemplateError: Bad for (no "in") in 'x y' at line 1 column 3
339+ >>> parse('{{py:x=1\ny=2}}')
340+ Traceback (most recent call last):
341+ ...
342+- TemplateError: Multi-line py blocks must start with a newline at line 1 column 3
343++ tempita.TemplateError: Multi-line py blocks must start with a newline at line 1 column 3
344+ """
345+ if delimiters is None:
346+ delimiters = ( Template.default_namespace['start_braces'],
347diff --git a/debian/patches/series b/debian/patches/series
348index acdaa7d..f128ea0 100644
349--- a/debian/patches/series
350+++ b/debian/patches/series
351@@ -1 +1,3 @@
352 0001-fix-encoding.patch
353+2to3.patch
354+fix-doc-tests.patch
355diff --git a/debian/rules b/debian/rules
356index 7513c22..cdd236b 100755
357--- a/debian/rules
358+++ b/debian/rules
359@@ -4,3 +4,6 @@ export PYBUILD_NAME = tempita
360
361 %:
362 dh $@ --buildsystem=pybuild --with python3
363+
364+override_dh_auto_test:
365+ nosetests3 -v

Subscribers

People subscribed via source and target branches