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