Author: Ronan Lamy <ronan.l...@gmail.com>
Branch: multiphase
Changeset: r92134:9adc44d5f032
Date: 2017-08-12 18:49 +0200
http://bitbucket.org/pypy/pypy/changeset/9adc44d5f032/

Log:    Allow non-ascii extension names (PEP 489)

diff --git a/pypy/module/cpyext/api.py b/pypy/module/cpyext/api.py
--- a/pypy/module/cpyext/api.py
+++ b/pypy/module/cpyext/api.py
@@ -26,6 +26,7 @@
 from pypy.interpreter.module import Module
 from pypy.interpreter.function import StaticMethod
 from pypy.objspace.std.sliceobject import W_SliceObject
+from pypy.objspace.std.unicodeobject import encode_object
 from pypy.module.__builtin__.descriptor import W_Property
 #from pypy.module.micronumpy.base import W_NDimArray
 from rpython.rlib.entrypoint import entrypoint_lowlevel
@@ -1476,12 +1477,11 @@
     # order of things here.
     from rpython.rlib import rdynload
 
-    name = space.text_w(space.getattr(w_spec, space.newtext("name")))
+    w_name = space.getattr(w_spec, space.newtext("name"))
     path = space.text_w(space.getattr(w_spec, space.newtext("origin")))
 
     if os.sep not in path:
         path = os.curdir + os.sep + path      # force a '/' in the path
-    basename = name.split('.')[-1]
     try:
         ll_libname = rffi.str2charp(path)
         try:
@@ -1489,13 +1489,14 @@
         finally:
             lltype.free(ll_libname, flavor='raw')
     except rdynload.DLOpenError as e:
-        w_name = space.newunicode(name.decode('ascii'))
         w_path = space.newfilename(path)
         raise raise_import_error(space,
             space.newfilename(e.msg), w_name, w_path)
     look_for = None
+    name = space.text_w(w_name)
     #
     if space.config.objspace.usemodules._cffi_backend:
+        basename = name.split('.')[-1]
         look_for = '_cffi_pypyinit_%s' % (basename,)
         try:
             initptr = rdynload.dlsym(dll, look_for)
@@ -1510,7 +1511,7 @@
                 raise
     #
     if space.config.objspace.usemodules.cpyext:
-        also_look_for = 'PyInit_%s' % (basename,)
+        also_look_for = get_init_name(space, w_name)
         try:
             initptr = rdynload.dlsym(dll, also_look_for)
         except KeyError:
@@ -1523,10 +1524,21 @@
             look_for = also_look_for
     msg = u"function %s not found in library %s" % (
         unicode(look_for), space.unicode_w(space.newfilename(path)))
-    w_name = space.newunicode(name.decode('ascii'))
     w_path = space.newfilename(path)
     raise_import_error(space, space.newunicode(msg), w_name, w_path)
 
+def get_init_name(space, w_name):
+    name_u = space.unicode_w(w_name)
+    basename_u = name_u.split(u'.')[-1]
+    try:
+        basename = basename_u.encode('ascii')
+        return 'PyInit_%s' % (basename,)
+    except UnicodeEncodeError:
+        basename = space.bytes_w(encode_object(
+            space, space.newunicode(basename_u), 'punycode', None))
+        basename = basename.replace('-', '_')
+        return 'PyInitU_%s' % (basename,)
+
 
 initfunctype = lltype.Ptr(lltype.FuncType([], PyObject))
 
@@ -1570,6 +1582,7 @@
                                                    name)
     finally:
         state.package_context = old_context
+    # XXX: should disable single-step init for non-ascii module names
     w_mod = get_w_obj_and_decref(space, initret)
     state.fixup_extension(w_mod, name, path)
     return w_mod
diff --git a/pypy/module/cpyext/test/test_module.py 
b/pypy/module/cpyext/test/test_module.py
--- a/pypy/module/cpyext/test/test_module.py
+++ b/pypy/module/cpyext/test/test_module.py
@@ -178,12 +178,13 @@
         importlib.reload(module)
         assert ex_class is module.Example
 
-    def w_load_from_name(self, name, origin=None):
+    def w_load_from_name(self, name, origin=None, use_prefix=True):
         from importlib import machinery, util
         if not origin:
             module = self.import_module(name=self.name)
             origin = module.__loader__.path
-        name = '_testmultiphase_' + name
+        if use_prefix:
+            name = '_testmultiphase_' + name
         loader = machinery.ExtensionFileLoader(name, origin)
         spec = util.spec_from_loader(name, loader)
         module = util.module_from_spec(spec)
@@ -228,3 +229,21 @@
         excinfo = raises(SystemError, self.load_from_name, 
'export_unreported_exception')
         assert "initialization" in excinfo.value.args[0]
         assert "unreported exception" in excinfo.value.args[0]
+
+    def test_unloadable_nonascii(self):
+        name = u"fo\xf3"
+        excinfo = raises(ImportError, self.load_from_name, name)
+        assert excinfo.value.name == '_testmultiphase_' + name
+
+    def test_nonascii(self):
+        module = self.import_module(name=self.name)
+        origin = module.__loader__.path
+        cases = [
+            ('_testmultiphase_zkou\u0161ka_na\u010dten\xed', 'Czech'),
+            ('\uff3f\u30a4\u30f3\u30dd\u30fc\u30c8\u30c6\u30b9\u30c8',
+             'Japanese'),
+            ]
+        for name, lang in cases:
+            module = self.load_from_name(name, origin=origin, use_prefix=False)
+            assert module.__name__ == name
+            assert module.__doc__ == "Module named in %s" % lang
_______________________________________________
pypy-commit mailing list
pypy-commit@python.org
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to