[pypy-commit] pypy py3k: add a decorator which allows to selectively use the equivalent of "from

antocuni noreply at buildbot.pypy.org
Wed Jul 18 22:09:35 CEST 2012


Author: Antonio Cuni <anto.cuni at gmail.com>
Branch: py3k
Changeset: r56193:401f486a2982
Date: 2012-07-18 21:34 +0200
http://bitbucket.org/pypy/pypy/changeset/401f486a2982/

Log:	add a decorator which allows to selectively use the equivalent of
	"from __future__ import unicode_literals" on a per-function basis

diff --git a/pypy/tool/sourcetools.py b/pypy/tool/sourcetools.py
--- a/pypy/tool/sourcetools.py
+++ b/pypy/tool/sourcetools.py
@@ -6,6 +6,7 @@
 # XXX We should try to generalize and single out one approach to dynamic
 # XXX code compilation.
 
+import types
 import sys, os, inspect, new
 import autopath, py
 
@@ -268,3 +269,29 @@
     except AttributeError:
         firstlineno = -1
     return "(%s:%d)%s" % (mod or '?', firstlineno, name or 'UNKNOWN')
+
+def with_unicode_literals(fn=None, **kwds):
+    encoding = kwds.pop('encoding', 'ascii')
+    if kwds:
+        raise TypeError("Unexpected keyword argument(s): %s" % ', '.join(kwds.keys()))
+    def decorator(fn):
+        co = fn.func_code
+        new_consts = []
+        for const in co.co_consts:
+            if isinstance(const, str):
+                const = const.decode(encoding)
+            new_consts.append(const)
+        new_consts = tuple(new_consts)
+        new_code = types.CodeType(co.co_argcount, co.co_nlocals, co.co_stacksize,
+                                  co.co_flags, co.co_code, new_consts, co.co_names,
+                                  co.co_varnames, co.co_filename, co.co_name,
+                                  co.co_firstlineno, co.co_lnotab)
+        fn.func_code = new_code
+        return fn
+    #
+    # support the usage of @with_unicode_literals instead of @with_unicode_literals()
+    if fn is not None:
+        assert type(fn) is types.FunctionType
+        return decorator(fn)
+    else:
+        return decorator
diff --git a/pypy/tool/test/test_sourcetools.py b/pypy/tool/test/test_sourcetools.py
--- a/pypy/tool/test/test_sourcetools.py
+++ b/pypy/tool/test/test_sourcetools.py
@@ -1,4 +1,6 @@
-from pypy.tool.sourcetools import func_with_new_name, func_renamer
+# -*- encoding: utf-8 -*-
+import py
+from pypy.tool.sourcetools import func_with_new_name, func_renamer, with_unicode_literals
 
 def test_rename():
     def f(x, y=5):
@@ -34,3 +36,24 @@
     bar3 = func_with_new_name(bar, 'bar3')
     assert bar3.func_doc == 'new doc'
     assert bar2.func_doc != bar3.func_doc
+
+
+def test_with_unicode_literals():
+    @with_unicode_literals()
+    def foo():
+        return 'hello'
+    assert type(foo()) is unicode
+    #
+    @with_unicode_literals
+    def foo():
+        return 'hello'
+    assert type(foo()) is unicode
+    #
+    def foo():
+        return 'hello &#224;&#232;&#236;'
+    py.test.raises(UnicodeDecodeError, "with_unicode_literals(foo)")
+    #
+    @with_unicode_literals(encoding='utf-8')
+    def foo():
+        return 'hello &#224;&#232;&#236;'
+    assert foo() == u'hello &#224;&#232;&#236;'


More information about the pypy-commit mailing list