Gentoo Archives: gentoo-commits

From: "Cédric Krier" <cedk@g.o>
To: gentoo-commits@l.g.o
Subject: [gentoo-commits] repo/gentoo:master commit in: dev-python/genshi/, dev-python/genshi/files/
Date: Sun, 30 Jul 2017 21:41:28
Message-Id: 1501450869.76b894c6c0001477235be97558d329e29ce8327f.cedk@gentoo
1 commit: 76b894c6c0001477235be97558d329e29ce8327f
2 Author: Cédric Krier <cedk <AT> gentoo <DOT> org>
3 AuthorDate: Sun Jul 30 21:41:09 2017 +0000
4 Commit: Cédric Krier <cedk <AT> gentoo <DOT> org>
5 CommitDate: Sun Jul 30 21:41:09 2017 +0000
6 URL: https://gitweb.gentoo.org/repo/gentoo.git/commit/?id=76b894c6
7
8 dev-python/genshi: Add Python3 support by applying patches
9
10 Gentoo-Bug: 616308
11
12 Package-Manager: Portage-2.3.6, Repoman-2.3.1
13
14 ...enshi-0.7-fix_tests_failure_with_python27.patch | 25 ++
15 dev-python/genshi/files/genshi-0.7-issue566.patch | 57 ++++
16 dev-python/genshi/files/genshi-0.7-issue582.patch | 364 +++++++++++++++++++++
17 dev-python/genshi/files/genshi-0.7-issue602.patch | 114 +++++++
18 dev-python/genshi/genshi-0.7-r1.ebuild | 46 +++
19 5 files changed, 606 insertions(+)
20
21 diff --git a/dev-python/genshi/files/genshi-0.7-fix_tests_failure_with_python27.patch b/dev-python/genshi/files/genshi-0.7-fix_tests_failure_with_python27.patch
22 new file mode 100644
23 index 00000000000..4f3467d6481
24 --- /dev/null
25 +++ b/dev-python/genshi/files/genshi-0.7-fix_tests_failure_with_python27.patch
26 @@ -0,0 +1,25 @@
27 +From 7f3552a9373fadd2d37ff592769ba6c65755eea5 Mon Sep 17 00:00:00 2001
28 +From: SVN-Git Migration <python-modules-team@×××××××××××××××××××.org>
29 +Date: Thu, 8 Oct 2015 09:13:47 -0700
30 +Subject: Skip test which still fails in Python 2.7.6.
31 +
32 +Author: Barry Warsaw <barry@××××××.org>, Arnaud Fontaine <arnau@××××××.org>
33 +Bug: http://genshi.edgewall.org/ticket/500
34 +
35 +Patch-Name: fix_tests_failure_with_python27.patch
36 +---
37 + genshi/filters/tests/test_html.py | 1 +
38 + 1 file changed, 1 insertion(+)
39 +
40 +diff --git a/genshi/filters/tests/test_html.py b/genshi/filters/tests/test_html.py
41 +index 0c6cfe1..a8cfa04 100644
42 +--- a/genshi/filters/tests/test_html.py
43 ++++ b/genshi/filters/tests/test_html.py
44 +@@ -410,6 +410,7 @@ class HTMLSanitizerTestCase(unittest.TestCase):
45 + html = HTML(u'&junk;')
46 + self.assertEquals('&amp;junk;', (html | HTMLSanitizer()).render())
47 +
48 ++ @unittest.skip('http://genshi.edgewall.org/ticket/500#comment:3')
49 + def test_sanitize_remove_script_elem(self):
50 + html = HTML(u'<script>alert("Foo")</script>')
51 + self.assertEquals('', (html | HTMLSanitizer()).render())
52
53 diff --git a/dev-python/genshi/files/genshi-0.7-issue566.patch b/dev-python/genshi/files/genshi-0.7-issue566.patch
54 new file mode 100644
55 index 00000000000..70fc8ea85e6
56 --- /dev/null
57 +++ b/dev-python/genshi/files/genshi-0.7-issue566.patch
58 @@ -0,0 +1,57 @@
59 +From fafbc4296902b2259c23d2ce55996b0127726b4f Mon Sep 17 00:00:00 2001
60 +From: SVN-Git Migration <python-modules-team@×××××××××××××××××××.org>
61 +Date: Thu, 8 Oct 2015 09:13:49 -0700
62 +Subject: Fix an IndexError preventing Genshi for uploading attachments in
63 +
64 + Trac for users with non-English language settings.
65 +Origin: http://genshi.edgewall.org/changeset/1243?format=diff&new=1243
66 +Bug: http://genshi.edgewall.org/ticket/566
67 +
68 +Patch-Name: issue566.patch
69 +---
70 + genshi/filters/i18n.py | 8 +++++++-
71 + genshi/filters/tests/i18n.py | 12 ++++++++++++
72 + 2 files changed, 19 insertions(+), 1 deletion(-)
73 +
74 +diff --git a/genshi/filters/i18n.py b/genshi/filters/i18n.py
75 +index dfb52b8..8f2d25c 100644
76 +--- a/genshi/filters/i18n.py
77 ++++ b/genshi/filters/i18n.py
78 +@@ -1048,7 +1048,13 @@ class MessageBuffer(object):
79 +
80 + while parts:
81 + order, string = parts.pop(0)
82 +- events = self.events[order].pop(0)
83 ++ events = self.events[order]
84 ++ if events:
85 ++ events = events.pop(0)
86 ++ else:
87 ++ # create a dummy empty text event so any remaining
88 ++ # part of the translation can be processed.
89 ++ events = [(TEXT, "", (None, -1, -1))]
90 + parts_counter[order].pop()
91 +
92 + for event in events:
93 +diff --git a/genshi/filters/tests/i18n.py b/genshi/filters/tests/i18n.py
94 +index 212d5f6..b36a30b 100644
95 +--- a/genshi/filters/tests/i18n.py
96 ++++ b/genshi/filters/tests/i18n.py
97 +@@ -928,6 +928,18 @@ class MsgDirectiveTestCase(unittest.TestCase):
98 + """</p></html>""",
99 + tmpl.generate(first="FIRST", second="SECOND").render())
100 +
101 ++ def test_translate_i18n_msg_ticket_404_regression(self):
102 ++ tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
103 ++ xmlns:i18n="http://genshi.edgewall.org/i18n">
104 ++ <h1 i18n:msg="name">text <a>$name</a></h1>
105 ++ </html>""")
106 ++ gettext = lambda s: u'head [1:%(name)s] tail'
107 ++ translator = Translator(gettext)
108 ++ translator.setup(tmpl)
109 ++ self.assertEqual("""<html>
110 ++ <h1>head <a>NAME</a> tail</h1>
111 ++ </html>""", tmpl.generate(name='NAME').render())
112 ++
113 +
114 + class ChooseDirectiveTestCase(unittest.TestCase):
115 +
116
117 diff --git a/dev-python/genshi/files/genshi-0.7-issue582.patch b/dev-python/genshi/files/genshi-0.7-issue582.patch
118 new file mode 100644
119 index 00000000000..fbcab626d6c
120 --- /dev/null
121 +++ b/dev-python/genshi/files/genshi-0.7-issue582.patch
122 @@ -0,0 +1,364 @@
123 +From 554fa3428bea3039decfd9064b860c753b2637a1 Mon Sep 17 00:00:00 2001
124 +From: SVN-Git Migration <python-modules-team@×××××××××××××××××××.org>
125 +Date: Thu, 8 Oct 2015 09:13:48 -0700
126 +Subject: Make genshi 0.7 compatible with Python 3.4.
127 +
128 +Origin: http://genshi.edgewall.org/changeset/1252?format=diff&new=1252
129 +Bug: http://genshi.edgewall.org/ticket/582
130 +Forwarded: not-needed
131 +
132 +Patch-Name: issue582.patch
133 +---
134 + doc/upgrade.txt | 8 ++---
135 + genshi/compat.py | 10 +++++-
136 + genshi/filters/tests/test_html.py | 14 ++++++---
137 + genshi/template/astutil.py | 66 ++++++++++++++++++++++++++++-----------
138 + genshi/template/eval.py | 37 +++++++++++++---------
139 + genshi/template/tests/eval.py | 23 ++++++++++++++
140 + run_benchmarks.sh | 31 ++++++++++++++++++
141 + setup.py | 6 +++-
142 + 8 files changed, 151 insertions(+), 44 deletions(-)
143 + create mode 100644 run_benchmarks.sh
144 +
145 +diff --git a/doc/upgrade.txt b/doc/upgrade.txt
146 +index b240eda..ad4c080 100644
147 +--- a/doc/upgrade.txt
148 ++++ b/doc/upgrade.txt
149 +@@ -7,11 +7,11 @@ Upgrading Genshi
150 + :depth: 2
151 + .. sectnum::
152 +
153 +-------------------------------------------------------
154 +-Upgrading from Genshi 0.6.x to the development version
155 +-------------------------------------------------------
156 ++-------------------------------------------
157 ++Upgrading from Genshi 0.6.x to Genshi 0.7.x
158 ++-------------------------------------------
159 +
160 +-The Genshi development version now supports both Python 2 and Python 3.
161 ++Genshi 0.7.x now supports both Python 2 and Python 3.
162 +
163 + The most noticable API change in the Genshi development version is that the
164 + default encoding in numerous places is now None (i.e. unicode) instead
165 +diff --git a/genshi/compat.py b/genshi/compat.py
166 +index 9787325..6574e39 100644
167 +--- a/genshi/compat.py
168 ++++ b/genshi/compat.py
169 +@@ -35,6 +35,15 @@ else:
170 + 'Python 2 compatibility function. Not usable in Python 3.')
171 +
172 +
173 ++# We need to test if an object is an instance of a string type in places
174 ++
175 ++if IS_PYTHON2:
176 ++ def isstring(obj):
177 ++ return isinstance(obj, basestring)
178 ++else:
179 ++ def isstring(obj):
180 ++ return isinstance(obj, str)
181 ++
182 + # We need to differentiate between StringIO and BytesIO in places
183 +
184 + if IS_PYTHON2:
185 +@@ -112,4 +121,3 @@ except NameError:
186 + if not x:
187 + return False
188 + return True
189 +-
190 +diff --git a/genshi/filters/tests/test_html.py b/genshi/filters/tests/test_html.py
191 +index a8cfa04..7120988 100644
192 +--- a/genshi/filters/tests/test_html.py
193 ++++ b/genshi/filters/tests/test_html.py
194 +@@ -368,12 +368,16 @@ def StyleSanitizer():
195 +
196 + class HTMLSanitizerTestCase(unittest.TestCase):
197 +
198 +- def assert_parse_error_or_equal(self, expected, exploit):
199 ++ def assert_parse_error_or_equal(self, expected, exploit,
200 ++ allow_strip=False):
201 + try:
202 + html = HTML(exploit)
203 + except ParseError:
204 + return
205 +- self.assertEquals(expected, (html | HTMLSanitizer()).render())
206 ++ sanitized_html = (html | HTMLSanitizer()).render()
207 ++ if not sanitized_html and allow_strip:
208 ++ return
209 ++ self.assertEquals(expected, sanitized_html)
210 +
211 + def test_sanitize_unchanged(self):
212 + html = HTML(u'<a href="#">fo<br />o</a>')
213 +@@ -417,10 +421,12 @@ class HTMLSanitizerTestCase(unittest.TestCase):
214 + html = HTML(u'<SCRIPT SRC="http://example.com/"></SCRIPT>')
215 + self.assertEquals('', (html | HTMLSanitizer()).render())
216 + src = u'<SCR\0IPT>alert("foo")</SCR\0IPT>'
217 +- self.assert_parse_error_or_equal('&lt;SCR\x00IPT&gt;alert("foo")', src)
218 ++ self.assert_parse_error_or_equal('&lt;SCR\x00IPT&gt;alert("foo")', src,
219 ++ allow_strip=True)
220 + src = u'<SCRIPT&XYZ SRC="http://example.com/"></SCRIPT>'
221 + self.assert_parse_error_or_equal('&lt;SCRIPT&amp;XYZ; '
222 +- 'SRC="http://example.com/"&gt;', src)
223 ++ 'SRC="http://example.com/"&gt;', src,
224 ++ allow_strip=True)
225 +
226 + def test_sanitize_remove_onclick_attr(self):
227 + html = HTML(u'<div onclick=\'alert("foo")\' />')
228 +diff --git a/genshi/template/astutil.py b/genshi/template/astutil.py
229 +index b24f728..e561846 100644
230 +--- a/genshi/template/astutil.py
231 ++++ b/genshi/template/astutil.py
232 +@@ -21,7 +21,7 @@ else:
233 + def parse(source, mode):
234 + return compile(source, '', mode, _ast.PyCF_ONLY_AST)
235 +
236 +-from genshi.compat import IS_PYTHON2
237 ++from genshi.compat import IS_PYTHON2, isstring
238 +
239 + __docformat__ = 'restructuredtext en'
240 +
241 +@@ -103,32 +103,48 @@ class ASTCodeGenerator(object):
242 + self._new_line()
243 + return self.visit(node.body)
244 +
245 ++ # Python < 3.4
246 + # arguments = (expr* args, identifier? vararg,
247 + # identifier? kwarg, expr* defaults)
248 ++ #
249 ++ # Python >= 3.4
250 ++ # arguments = (arg* args, arg? vararg, arg* kwonlyargs, expr* kw_defaults,
251 ++ # arg? kwarg, expr* defaults)
252 + def visit_arguments(self, node):
253 +- first = True
254 +- no_default_count = len(node.args) - len(node.defaults)
255 +- for i, arg in enumerate(node.args):
256 +- if not first:
257 +- self._write(', ')
258 ++ def write_possible_comma():
259 ++ if _first[0]:
260 ++ _first[0] = False
261 + else:
262 +- first = False
263 +- self.visit(arg)
264 +- if i >= no_default_count:
265 +- self._write('=')
266 +- self.visit(node.defaults[i - no_default_count])
267 +- if getattr(node, 'vararg', None):
268 +- if not first:
269 + self._write(', ')
270 ++ _first = [True]
271 ++
272 ++ def write_args(args, defaults):
273 ++ no_default_count = len(args) - len(defaults)
274 ++ for i, arg in enumerate(args):
275 ++ write_possible_comma()
276 ++ self.visit(arg)
277 ++ default_idx = i - no_default_count
278 ++ if default_idx >= 0 and defaults[default_idx] is not None:
279 ++ self._write('=')
280 ++ self.visit(defaults[i - no_default_count])
281 ++
282 ++ write_args(node.args, node.defaults)
283 ++ if getattr(node, 'vararg', None):
284 ++ write_possible_comma()
285 ++ self._write('*')
286 ++ if isstring(node.vararg):
287 ++ self._write(node.vararg)
288 + else:
289 +- first = False
290 +- self._write('*' + node.vararg)
291 ++ self.visit(node.vararg)
292 ++ if getattr(node, 'kwonlyargs', None):
293 ++ write_args(node.kwonlyargs, node.kw_defaults)
294 + if getattr(node, 'kwarg', None):
295 +- if not first:
296 +- self._write(', ')
297 ++ write_possible_comma()
298 ++ self._write('**')
299 ++ if isstring(node.kwarg):
300 ++ self._write(node.kwarg)
301 + else:
302 +- first = False
303 +- self._write('**' + node.kwarg)
304 ++ self.visit(node.kwarg)
305 +
306 + if not IS_PYTHON2:
307 + # In Python 3 arguments get a special node
308 +@@ -732,6 +748,17 @@ class ASTCodeGenerator(object):
309 + def visit_Name(self, node):
310 + self._write(node.id)
311 +
312 ++ # NameConstant(singleton value)
313 ++ def visit_NameConstant(self, node):
314 ++ if node.value is None:
315 ++ self._write('None')
316 ++ elif node.value is True:
317 ++ self._write('True')
318 ++ elif node.value is False:
319 ++ self._write('False')
320 ++ else:
321 ++ raise Exception("Unknown NameConstant %r" % (node.value,))
322 ++
323 + # List(expr* elts, expr_context ctx)
324 + def visit_List(self, node):
325 + self._write('[')
326 +@@ -837,6 +864,7 @@ class ASTTransformer(object):
327 + visit_Attribute = _clone
328 + visit_Subscript = _clone
329 + visit_Name = _clone
330 ++ visit_NameConstant = _clone
331 + visit_List = _clone
332 + visit_Tuple = _clone
333 +
334 +diff --git a/genshi/template/eval.py b/genshi/template/eval.py
335 +index c00cfcb..81644a7 100644
336 +--- a/genshi/template/eval.py
337 ++++ b/genshi/template/eval.py
338 +@@ -24,7 +24,8 @@ from genshi.template.astutil import ASTTransformer, ASTCodeGenerator, \
339 + from genshi.template.base import TemplateRuntimeError
340 + from genshi.util import flatten
341 +
342 +-from genshi.compat import get_code_params, build_code_chunk, IS_PYTHON2
343 ++from genshi.compat import get_code_params, build_code_chunk, isstring, \
344 ++ IS_PYTHON2
345 +
346 + __all__ = ['Code', 'Expression', 'Suite', 'LenientLookup', 'StrictLookup',
347 + 'Undefined', 'UndefinedError']
348 +@@ -495,28 +496,34 @@ class TemplateASTTransformer(ASTTransformer):
349 + def __init__(self):
350 + self.locals = [CONSTANTS]
351 +
352 ++ def _process(self, names, node):
353 ++ if not IS_PYTHON2 and isinstance(node, _ast.arg):
354 ++ names.add(node.arg)
355 ++ elif isstring(node):
356 ++ names.add(node)
357 ++ elif isinstance(node, _ast.Name):
358 ++ names.add(node.id)
359 ++ elif isinstance(node, _ast.alias):
360 ++ names.add(node.asname or node.name)
361 ++ elif isinstance(node, _ast.Tuple):
362 ++ for elt in node.elts:
363 ++ self._process(names, elt)
364 ++
365 + def _extract_names(self, node):
366 + names = set()
367 +- def _process(node):
368 +- if not IS_PYTHON2 and isinstance(node, _ast.arg):
369 +- names.add(node.arg)
370 +- if isinstance(node, _ast.Name):
371 +- names.add(node.id)
372 +- elif isinstance(node, _ast.alias):
373 +- names.add(node.asname or node.name)
374 +- elif isinstance(node, _ast.Tuple):
375 +- for elt in node.elts:
376 +- _process(elt)
377 + if hasattr(node, 'args'):
378 + for arg in node.args:
379 +- _process(arg)
380 ++ self._process(names, arg)
381 ++ if hasattr(node, 'kwonlyargs'):
382 ++ for arg in node.kwonlyargs:
383 ++ self._process(names, arg)
384 + if hasattr(node, 'vararg'):
385 +- names.add(node.vararg)
386 ++ self._process(names, node.vararg)
387 + if hasattr(node, 'kwarg'):
388 +- names.add(node.kwarg)
389 ++ self._process(names, node.kwarg)
390 + elif hasattr(node, 'names'):
391 + for elt in node.names:
392 +- _process(elt)
393 ++ self._process(names, elt)
394 + return names
395 +
396 + def visit_Str(self, node):
397 +diff --git a/genshi/template/tests/eval.py b/genshi/template/tests/eval.py
398 +index 7722571..c44a0e3 100644
399 +--- a/genshi/template/tests/eval.py
400 ++++ b/genshi/template/tests/eval.py
401 +@@ -590,6 +590,29 @@ x = smash(foo='abc', bar='def')
402 + suite.execute(data)
403 + self.assertEqual(['bardef', 'fooabc'], sorted(data['x']))
404 +
405 ++ if not IS_PYTHON2:
406 ++ def test_def_kwonlyarg(self):
407 ++ suite = Suite("""
408 ++def kwonly(*args, k):
409 ++ return k
410 ++x = kwonly(k="foo")
411 ++""")
412 ++ data = {}
413 ++ suite.execute(data)
414 ++ self.assertEqual("foo", data['x'])
415 ++
416 ++ def test_def_kwonlyarg_with_default(self):
417 ++ suite = Suite("""
418 ++def kwonly(*args, k="bar"):
419 ++ return k
420 ++x = kwonly(k="foo")
421 ++y = kwonly()
422 ++""")
423 ++ data = {}
424 ++ suite.execute(data)
425 ++ self.assertEqual("foo", data['x'])
426 ++ self.assertEqual("bar", data['y'])
427 ++
428 + def test_def_nested(self):
429 + suite = Suite("""
430 + def doit():
431 +diff --git a/run_benchmarks.sh b/run_benchmarks.sh
432 +new file mode 100644
433 +index 0000000..0c64cc8
434 +--- /dev/null
435 ++++ b/run_benchmarks.sh
436 +@@ -0,0 +1,31 @@
437 ++#!/bin/sh
438 ++#
439 ++# 1. Run the tests with `tox` (this will set up all the tox envs).
440 ++# 2. ./run_benchmarks.sh <env-name> | tee results-<env-name>.out
441 ++
442 ++NAME="$1"
443 ++PYTHON="./.tox/$NAME/bin/python"
444 ++BENCH_DIR="bench_build/$1"
445 ++BENCH_BIN_DIR="$BENCH_DIR/bin"
446 ++mkdir -p "bench_build"
447 ++
448 ++rm -rf "$BENCH_DIR"
449 ++cp -R "examples/bench" "$BENCH_DIR"
450 ++
451 ++case "$NAME" in
452 ++ py32|py33)
453 ++ 2to3 -w --no-diffs "$BENCH_DIR"
454 ++ ;;
455 ++esac
456 ++
457 ++echo "-- basic --"
458 ++"$PYTHON" "$BENCH_DIR/basic.py"
459 ++echo
460 ++
461 ++echo "-- bigtable --"
462 ++"$PYTHON" "$BENCH_DIR/bigtable.py"
463 ++echo
464 ++
465 ++echo "-- xpath --"
466 ++"$PYTHON" "$BENCH_DIR/xpath.py"
467 ++echo
468 +diff --git a/setup.py b/setup.py
469 +index 294ba9b..45099b5 100755
470 +--- a/setup.py
471 ++++ b/setup.py
472 +@@ -65,9 +65,13 @@ available.""")
473 +
474 +
475 + if Feature:
476 ++ # Optional C extension module for speeding up Genshi:
477 ++ # Not activated by default on:
478 ++ # - PyPy (where it harms performance)
479 ++ # - CPython >= 3.3 (the new Unicode C API is not supported yet)
480 + speedups = Feature(
481 + "optional C speed-enhancements",
482 +- standard = not is_pypy,
483 ++ standard = not is_pypy and sys.version_info < (3, 3),
484 + ext_modules = [
485 + Extension('genshi._speedups', ['genshi/_speedups.c']),
486 + ],
487
488 diff --git a/dev-python/genshi/files/genshi-0.7-issue602.patch b/dev-python/genshi/files/genshi-0.7-issue602.patch
489 new file mode 100644
490 index 00000000000..d7f0b77fa92
491 --- /dev/null
492 +++ b/dev-python/genshi/files/genshi-0.7-issue602.patch
493 @@ -0,0 +1,114 @@
494 +From 1acbd00b4961164edc8a185458ba4a433bedbceb Mon Sep 17 00:00:00 2001
495 +From: SVN-Git Migration <python-modules-team@×××××××××××××××××××.org>
496 +Date: Thu, 8 Oct 2015 09:13:46 -0700
497 +Subject: Fix Python 3.5 compatibility issues.
498 +
499 +Origin: http://genshi.edgewall.org/attachment/ticket/602/t602.diff
500 +Bug: http://genshi.edgewall.org/ticket/602
501 +Forwarded: not-needed
502 +
503 +Patch-Name: issue602.patch
504 +---
505 + genshi/filters/i18n.py | 6 ++++--
506 + genshi/template/astutil.py | 14 +++++++++++---
507 + genshi/template/directives.py | 20 ++++++++++++++------
508 + genshi/template/eval.py | 5 +++++
509 + 4 files changed, 34 insertions(+), 11 deletions(-)
510 +
511 +diff --git a/genshi/filters/i18n.py b/genshi/filters/i18n.py
512 +index b724956..dfb52b8 100644
513 +--- a/genshi/filters/i18n.py
514 ++++ b/genshi/filters/i18n.py
515 +@@ -1187,8 +1187,10 @@ def extract_from_code(code, gettext_functions):
516 + elif arg:
517 + strings.append(None)
518 + [_add(arg) for arg in node.args]
519 +- _add(node.starargs)
520 +- _add(node.kwargs)
521 ++ if hasattr(node, 'starargs'):
522 ++ _add(node.starargs)
523 ++ if hasattr(node, 'kwargs'):
524 ++ _add(node.kwargs)
525 + if len(strings) == 1:
526 + strings = strings[0]
527 + else:
528 +diff --git a/genshi/template/astutil.py b/genshi/template/astutil.py
529 +index a4c21c8..b24f728 100644
530 +--- a/genshi/template/astutil.py
531 ++++ b/genshi/template/astutil.py
532 +@@ -135,6 +135,10 @@ class ASTCodeGenerator(object):
533 + def visit_arg(self, node):
534 + self._write(node.arg)
535 +
536 ++ def visit_Starred(self, node):
537 ++ self._write('*')
538 ++ self.visit(node.value)
539 ++
540 + # FunctionDef(identifier name, arguments args,
541 + # stmt* body, expr* decorator_list)
542 + def visit_FunctionDef(self, node):
543 +@@ -648,9 +652,13 @@ class ASTCodeGenerator(object):
544 + if not first:
545 + self._write(', ')
546 + first = False
547 +- # keyword = (identifier arg, expr value)
548 +- self._write(keyword.arg)
549 +- self._write('=')
550 ++ if not keyword.arg:
551 ++ # Python 3.5+ star-star args
552 ++ self._write('**')
553 ++ else:
554 ++ # keyword = (identifier arg, expr value)
555 ++ self._write(keyword.arg)
556 ++ self._write('=')
557 + self.visit(keyword.value)
558 + if getattr(node, 'starargs', None):
559 + if not first:
560 +diff --git a/genshi/template/directives.py b/genshi/template/directives.py
561 +index 7301c2d..1f70ef6 100644
562 +--- a/genshi/template/directives.py
563 ++++ b/genshi/template/directives.py
564 +@@ -266,13 +266,21 @@ class DefDirective(Directive):
565 + if isinstance(ast, _ast.Call):
566 + self.name = ast.func.id
567 + for arg in ast.args:
568 +- # only names
569 +- self.args.append(arg.id)
570 ++ if hasattr(_ast, 'Starred') and isinstance(arg, _ast.Starred):
571 ++ # Python 3.5+
572 ++ self.star_args = arg.value.id
573 ++ else:
574 ++ # only names
575 ++ self.args.append(arg.id)
576 + for kwd in ast.keywords:
577 +- self.args.append(kwd.arg)
578 +- exp = Expression(kwd.value, template.filepath,
579 +- lineno, lookup=template.lookup)
580 +- self.defaults[kwd.arg] = exp
581 ++ if kwd.arg is None:
582 ++ # Python 3.5+
583 ++ self.dstar_args = kwd.value.id
584 ++ else:
585 ++ self.args.append(kwd.arg)
586 ++ exp = Expression(kwd.value, template.filepath,
587 ++ lineno, lookup=template.lookup)
588 ++ self.defaults[kwd.arg] = exp
589 + if getattr(ast, 'starargs', None):
590 + self.star_args = ast.starargs.id
591 + if getattr(ast, 'kwargs', None):
592 +diff --git a/genshi/template/eval.py b/genshi/template/eval.py
593 +index 89aec49..c00cfcb 100644
594 +--- a/genshi/template/eval.py
595 ++++ b/genshi/template/eval.py
596 +@@ -593,6 +593,11 @@ class TemplateASTTransformer(ASTTransformer):
597 + finally:
598 + self.locals.pop()
599 +
600 ++ # Only used in Python 3.5+
601 ++ def visit_Starred(self, node):
602 ++ node.value = self.visit(node.value)
603 ++ return node
604 ++
605 + def visit_Name(self, node):
606 + # If the name refers to a local inside a lambda, list comprehension, or
607 + # generator expression, leave it alone
608
609 diff --git a/dev-python/genshi/genshi-0.7-r1.ebuild b/dev-python/genshi/genshi-0.7-r1.ebuild
610 new file mode 100644
611 index 00000000000..65360402e2c
612 --- /dev/null
613 +++ b/dev-python/genshi/genshi-0.7-r1.ebuild
614 @@ -0,0 +1,46 @@
615 +# Copyright 1999-2017 Gentoo Foundation
616 +# Distributed under the terms of the GNU General Public License v2
617 +
618 +EAPI=5
619 +PYTHON_COMPAT=( python{2_7,3_{4,5,6}} pypy )
620 +
621 +inherit distutils-r1
622 +
623 +MY_P="Genshi-${PV}"
624 +
625 +DESCRIPTION="Python toolkit for stream-based generation of output for the web"
626 +HOMEPAGE="http://genshi.edgewall.org/ https://pypi.python.org/pypi/Genshi"
627 +SRC_URI="http://ftp.edgewall.com/pub/genshi/${MY_P}.tar.gz"
628 +
629 +LICENSE="BSD"
630 +SLOT="0"
631 +KEYWORDS="~amd64 ~ppc ~ppc64 ~sparc ~x86 ~x86-fbsd ~amd64-linux ~x86-linux ~x64-macos ~x86-macos"
632 +IUSE="doc examples"
633 +
634 +DEPEND="dev-python/setuptools[${PYTHON_USEDEP}]"
635 +RDEPEND="${DEPEND}"
636 +
637 +S="${WORKDIR}/${MY_P}"
638 +
639 +src_prepare() {
640 + epatch "${FILESDIR}/${P}-issue566.patch"
641 + epatch "${FILESDIR}/${P}-issue582.patch"
642 + epatch "${FILESDIR}/${P}-fix_tests_failure_with_python27.patch"
643 + epatch "${FILESDIR}/${P}-issue602.patch"
644 +}
645 +
646 +python_test() {
647 + esetup.py test
648 +}
649 +
650 +python_install_all() {
651 + if use doc; then
652 + dodoc doc/*.txt
653 + dohtml -r doc/*
654 + fi
655 + if use examples; then
656 + insinto /usr/share/doc/${PF}
657 + doins -r examples
658 + fi
659 + distutils-r1_python_install_all
660 +}