Author: Armin Rigo <ar...@tunes.org> Branch: py3.5 Changeset: r86364:7d8da1e4bd2c Date: 2016-08-20 23:11 +0200 http://bitbucket.org/pypy/pypy/changeset/7d8da1e4bd2c/
Log: hg merge py3k diff --git a/pypy/interpreter/eval.py b/pypy/interpreter/eval.py --- a/pypy/interpreter/eval.py +++ b/pypy/interpreter/eval.py @@ -40,9 +40,6 @@ and possibly more locals.""" return self.signature().getallvarnames() - def getformalargcount(self): - return self.signature().scope_length() - def getdocstring(self, space): return space.w_None diff --git a/pypy/interpreter/function.py b/pypy/interpreter/function.py --- a/pypy/interpreter/function.py +++ b/pypy/interpreter/function.py @@ -38,7 +38,9 @@ 'name?', 'w_kw_defs?'] - def __init__(self, space, code, w_globals=None, defs_w=[], w_kw_defs=None, + w_kw_defs = None + + def __init__(self, space, code, w_globals=None, defs_w=[], kw_defs_w=None, closure=None, w_ann=None, forcename=None, qualname=None): self.space = space self.name = forcename or code.co_name @@ -48,10 +50,12 @@ self.w_func_globals = w_globals # the globals dictionary self.closure = closure # normally, list of Cell instances or None self.defs_w = defs_w - self.w_kw_defs = w_kw_defs self.w_func_dict = None # filled out below if needed self.w_module = None self.w_ann = w_ann + # + if kw_defs_w is not None: + self.init_kwdefaults_dict(kw_defs_w) def __repr__(self): # return "function %s.%s" % (self.space, self.name) @@ -379,14 +383,29 @@ def fset_func_kwdefaults(self, space, w_new): if space.is_w(w_new, space.w_None): - w_new = None - elif not space.isinstance_w(w_new, space.w_dict): - raise oefmt(space.w_TypeError, "__kwdefaults__ must be a dict") - self.w_kw_defs = w_new + self.w_kw_defs = None + else: + if not space.isinstance_w(w_new, space.w_dict): + raise oefmt(space.w_TypeError, "__kwdefaults__ must be a dict") + w_instance = self.init_kwdefaults_dict() + w_instance.setdict(space, w_new) + self.w_kw_defs = w_instance.getdict(space) def fdel_func_kwdefaults(self, space): self.w_kw_defs = None + def init_kwdefaults_dict(self, kw_defs_w=[]): + # use the mapdict logic to get at least not-too-bad JIT code + # from function calls with default values of kwonly arguments + space = self.space + w_class = space.fromcache(KwDefsClassCache).w_class + w_instance = space.call_function(w_class) + for w_name, w_value in kw_defs_w: + attr = space.unicode_w(w_name).encode('utf-8') + w_instance.setdictvalue(space, attr, w_value) + self.w_kw_defs = w_instance.getdict(space) + return w_instance + def fget_func_doc(self, space): if self.w_doc is None: self.w_doc = self.code.getdocstring(space) @@ -663,10 +682,12 @@ def __init__(self, func): assert isinstance(func, Function) Function.__init__(self, func.space, func.code, func.w_func_globals, - func.defs_w, None, func.closure, None, func.name) + func.defs_w, None, func.closure, + None, func.name) self.w_doc = func.w_doc self.w_func_dict = func.w_func_dict self.w_module = func.w_module + self.w_kw_defs = func.w_kw_defs def descr_builtinfunction__new__(space, w_subtype): raise oefmt(space.w_TypeError, @@ -684,3 +705,12 @@ else: code = None return isinstance(code, BuiltinCode) + + +class KwDefsClassCache: + def __init__(self, space): + self.w_class = space.appexec([], """(): + class KwDefs: + pass + return KwDefs + """) diff --git a/pypy/interpreter/gateway.py b/pypy/interpreter/gateway.py --- a/pypy/interpreter/gateway.py +++ b/pypy/interpreter/gateway.py @@ -28,6 +28,8 @@ from rpython.rlib.rarithmetic import r_longlong, r_int, r_ulonglong, r_uint from rpython.tool.sourcetools import func_with_new_name, compile2 +NO_DEFAULT = object() + # internal non-translatable parts: class SignatureBuilder(object): @@ -44,12 +46,21 @@ self.argnames = argnames self.varargname = varargname self.kwargname = kwargname + self.kwonlyargnames = None def append(self, argname): - self.argnames.append(argname) + if self.kwonlyargnames is None: + self.argnames.append(argname) + else: + self.kwonlyargnames.append(argname) + + def marker_kwonly(self): + assert self.kwonlyargnames is None + self.kwonlyargnames = [] def signature(self): - return Signature(self.argnames, self.varargname, self.kwargname) + return Signature(self.argnames, self.varargname, self.kwargname, + self.kwonlyargnames) #________________________________________________________________ @@ -66,13 +77,6 @@ """NOT_RPYTHON""" raise NotImplementedError -def kwonly(arg_unwrapper): - """Mark argument as keyword-only. - - XXX: has no actual effect for now. - """ - return arg_unwrapper - class UnwrapSpecRecipe(object): "NOT_RPYTHON" @@ -229,6 +233,11 @@ name = int_unwrapping_space_method(typ) self.checked_space_method(name, app_sig) + def visit_kwonly(self, _, app_sig): + argname = self.orig_arg() + assert argname == '__kwonly__' + app_sig.marker_kwonly() + class UnwrapSpec_EmitRun(UnwrapSpecEmit): @@ -316,6 +325,9 @@ def visit_truncatedint_w(self, typ): self.run_args.append("space.truncatedint_w(%s)" % (self.scopenext(),)) + def visit_kwonly(self, typ): + self.run_args.append("None") + def _make_unwrap_activation_class(self, unwrap_spec, cache={}): try: key = tuple(unwrap_spec) @@ -468,6 +480,9 @@ def visit_truncatedint_w(self, typ): self.unwrap.append("space.truncatedint_w(%s)" % (self.nextarg(),)) + def visit_kwonly(self, typ): + raise FastFuncNotSupported + def make_fastfunc(unwrap_spec, func): unwrap_info = UnwrapSpec_FastFunc_Unwrap() unwrap_info.apply_over(unwrap_spec) @@ -563,6 +578,8 @@ unwrap_spec.append('args_w') elif argname.startswith('w_'): unwrap_spec.append(W_Root) + elif argname == '__kwonly__': + unwrap_spec.append('kwonly') else: unwrap_spec.append(None) @@ -616,6 +633,8 @@ argnames = sig.argnames varargname = sig.varargname kwargname = sig.kwargname + if sig.kwonlyargnames: + import pdb; pdb.set_trace() self._argnames = argnames if unwrap_spec is None: @@ -950,64 +969,71 @@ self.name = app_name self.as_classmethod = as_classmethod - if not f.func_defaults: - self._staticdefs = [] - else: - argnames = self._code._argnames - defaults = f.func_defaults - self._staticdefs = zip(argnames[-len(defaults):], defaults) + argnames = self._code._argnames + defaults = f.func_defaults or () + self._staticdefs = dict(zip( + argnames[len(argnames) - len(defaults):], defaults)) + return self def _getdefaults(self, space): "NOT_RPYTHON" - defs_w = [] - unwrap_spec = self._code._unwrap_spec[-len(self._staticdefs):] - for i, (name, defaultval) in enumerate(self._staticdefs): + alldefs_w = {} + assert len(self._code._argnames) == len(self._code._unwrap_spec) + for name, spec in zip(self._code._argnames, self._code._unwrap_spec): + if name == '__kwonly__': + continue + + defaultval = self._staticdefs.get(name, NO_DEFAULT) + w_def = Ellipsis if name.startswith('w_'): - assert defaultval is None, ( + assert defaultval in (NO_DEFAULT, None), ( "%s: default value for '%s' can only be None, got %r; " "use unwrap_spec(...=WrappedDefault(default))" % ( self._code.identifier, name, defaultval)) - defs_w.append(None) - elif name != '__args__' and name != 'args_w': - spec = unwrap_spec[i] - if isinstance(defaultval, str) and spec not in [str]: - defs_w.append(space.newbytes(defaultval)) - else: - defs_w.append(space.wrap(defaultval)) - if self._code._unwrap_spec: - UNDEFINED = object() - alldefs_w = [UNDEFINED] * len(self._code.sig.argnames) - if defs_w: - alldefs_w[-len(defs_w):] = defs_w - code = self._code - assert isinstance(code._unwrap_spec, (list, tuple)) - assert isinstance(code._argnames, list) - assert len(code._unwrap_spec) == len(code._argnames) - for i in range(len(code._unwrap_spec)-1, -1, -1): - spec = code._unwrap_spec[i] - argname = code._argnames[i] - if isinstance(spec, tuple) and spec[0] is W_Root: - assert False, "use WrappedDefault" - if isinstance(spec, WrappedDefault): - default_value = spec.default_value - if isinstance(default_value, str): - w_default = space.newbytes(default_value) - else: - w_default = space.wrap(default_value) - assert isinstance(w_default, W_Root) - assert argname.startswith('w_') - argname = argname[2:] - j = self._code.sig.argnames.index(argname) - assert alldefs_w[j] in (UNDEFINED, None) - alldefs_w[j] = w_default - first_defined = 0 - while (first_defined < len(alldefs_w) and - alldefs_w[first_defined] is UNDEFINED): - first_defined += 1 - defs_w = alldefs_w[first_defined:] - assert UNDEFINED not in defs_w - return defs_w + if defaultval is None: + w_def = None + + if isinstance(spec, tuple) and spec[0] is W_Root: + assert False, "use WrappedDefault" + elif isinstance(spec, WrappedDefault): + assert name.startswith('w_') + defaultval = spec.default_value + w_def = Ellipsis + + if defaultval is not NO_DEFAULT: + if name != '__args__' and name != 'args_w': + if w_def is Ellipsis: + if isinstance(defaultval, str) and spec not in [str]: + w_def = space.newbytes(defaultval) + else: + w_def = space.wrap(defaultval) + if name.startswith('w_'): + name = name[2:] + alldefs_w[name] = w_def + # + # Here, 'alldefs_w' maps some argnames to their wrapped default + # value. We return two lists: + # - a list of defaults for positional arguments, which covers + # some suffix of the sig.argnames list + # - a list of pairs (w_name, w_def) for kwonly arguments + # + sig = self._code.sig + first_defined = 0 + while (first_defined < len(sig.argnames) and + sig.argnames[first_defined] not in alldefs_w): + first_defined += 1 + defs_w = [alldefs_w.pop(name) for name in sig.argnames[first_defined:]] + + kw_defs_w = None + if alldefs_w: + kw_defs_w = [] + for name, w_def in sorted(alldefs_w.items()): + assert name in sig.kwonlyargnames + w_name = space.newunicode(name.decode('utf-8')) + kw_defs_w.append((w_name, w_def)) + + return defs_w, kw_defs_w # lazy binding to space @@ -1027,9 +1053,10 @@ def build(cache, gateway): "NOT_RPYTHON" space = cache.space - defs = gateway._getdefaults(space) # needs to be implemented by subclass + defs_w, kw_defs_w = gateway._getdefaults(space) code = gateway._code - fn = FunctionWithFixedCode(space, code, None, defs, forcename=gateway.name) + fn = FunctionWithFixedCode(space, code, None, defs_w, kw_defs_w, + forcename=gateway.name) if not space.config.translating: fn.add_to_table() if gateway.as_classmethod: @@ -1167,15 +1194,15 @@ source = source[source.find('\n') + 1:].lstrip() assert source.startswith("def "), "can only transform functions" source = source[4:] - import __future__ - if flags & __future__.CO_FUTURE_DIVISION: - prefix += "from __future__ import division\n" - if flags & __future__.CO_FUTURE_ABSOLUTE_IMPORT: - prefix += "from __future__ import absolute_import\n" - if flags & __future__.CO_FUTURE_PRINT_FUNCTION: - prefix += "from __future__ import print_function\n" - if flags & __future__.CO_FUTURE_UNICODE_LITERALS: - prefix += "from __future__ import unicode_literals\n" + # The following flags have no effect any more in app-level code + # (i.e. they are always on anyway), and have been removed: + # CO_FUTURE_DIVISION + # CO_FUTURE_ABSOLUTE_IMPORT + # CO_FUTURE_PRINT_FUNCTION + # CO_FUTURE_UNICODE_LITERALS + # Original code was, for each of these flags: + # if flags & __future__.CO_xxx: + # prefix += "from __future__ import yyy\n" p = source.find('(') assert p >= 0 funcname = source[:p].strip() diff --git a/pypy/interpreter/mixedmodule.py b/pypy/interpreter/mixedmodule.py --- a/pypy/interpreter/mixedmodule.py +++ b/pypy/interpreter/mixedmodule.py @@ -19,6 +19,7 @@ """ NOT_RPYTHON """ Module.__init__(self, space, w_name) self.lazy = True + self.lazy_initial_values_w = {} self.__class__.buildloaders() self.loaders = self.loaders.copy() # copy from the class to the inst self.submodules_w = [] @@ -57,22 +58,11 @@ if not self.lazy and self.w_initialdict is None: self.save_module_content_for_future_reload() - def save_module_content_for_future_reload(self, save_all=False): - # Because setdictvalue is unable to immediately load all attributes - # (due to an importlib bootstrapping problem), this method needs to be - # able to support saving the content of a module's dict without - # requiring that the entire dict already be loaded. To support that - # properly, when updating the dict, we must be careful to never - # overwrite the value of a key already in w_initialdict. (So as to avoid - # overriding the builtin value with a user-provided value) - if self.space.is_none(self.w_initialdict) or save_all: - self.w_initialdict = self.space.call_method(self.w_dict, 'copy') - else: - w_items = self.space.call_method(self.w_dict, 'items') - for w_item in self.space.iteriterable(w_items): - w_key, w_value = self.space.fixedview(w_item, expected_length=2) - if not self.space.contains_w(self.w_initialdict, w_key): - self.space.setitem(self.w_initialdict, w_key, w_value) + def save_module_content_for_future_reload(self): + # Save the current dictionary in w_initialdict, for future + # reloads. This forces the dictionary if needed. + w_dict = self.getdict(self.space) + self.w_initialdict = self.space.call_method(w_dict, 'copy') @classmethod def get_applevel_name(cls): @@ -101,9 +91,13 @@ return w_value def setdictvalue(self, space, attr, w_value): - if self.lazy: - self._load_lazily(space, attr) - self.save_module_content_for_future_reload() + if self.lazy and attr not in self.lazy_initial_values_w: + # in lazy mode, the first time an attribute changes, + # we save away the old (initial) value. This allows + # a future getdict() call to build the correct + # self.w_initialdict, containing the initial value. + w_initial_value = self._load_lazily(space, attr) + self.lazy_initial_values_w[attr] = w_initial_value space.setitem_str(self.w_dict, attr, w_value) return True @@ -137,13 +131,29 @@ def getdict(self, space): if self.lazy: - for name in self.loaders: - w_value = self.get(name) - space.setitem(self.w_dict, space.new_interned_str(name), w_value) - self.lazy = False - self.save_module_content_for_future_reload() + self._force_lazy_dict_now() return self.w_dict + def _force_lazy_dict_now(self): + # Force the dictionary by calling all lazy loaders now. + # This also saves in self.w_initialdict a copy of all the + # initial values, including if they have already been + # modified by setdictvalue(). + space = self.space + for name in self.loaders: + w_value = self.get(name) + space.setitem(self.w_dict, space.new_interned_str(name), w_value) + self.lazy = False + self.save_module_content_for_future_reload() + for key, w_initial_value in self.lazy_initial_values_w.items(): + w_key = space.new_interned_str(key) + if w_initial_value is not None: + space.setitem(self.w_initialdict, w_key, w_initial_value) + else: + if space.finditem(self.w_initialdict, w_key) is not None: + space.delitem(self.w_initialdict, w_key) + del self.lazy_initial_values_w + def _cleanup_(self): self.getdict(self.space) self.w_initialdict = None diff --git a/pypy/interpreter/pyopcode.py b/pypy/interpreter/pyopcode.py --- a/pypy/interpreter/pyopcode.py +++ b/pypy/interpreter/pyopcode.py @@ -1288,15 +1288,15 @@ for i in range(len(names_w) - 1, -1, -1): space.setitem(w_ann, names_w[i], self.popvalue()) defaultarguments = self.popvalues(posdefaults) - w_kw_defs = None + kw_defs_w = None if kwdefaults: - w_kw_defs = space.newdict(strdict=True) - for i in range(kwdefaults - 1, -1, -1): - w_name = self.popvalue() - w_def = self.popvalue() - space.setitem(w_kw_defs, w_def, w_name) + kw_defs_w = [] + for i in range(kwdefaults): + w_defvalue = self.popvalue() + w_defname = self.popvalue() + kw_defs_w.append((w_defname, w_defvalue)) fn = function.Function(space, codeobj, self.get_w_globals(), defaultarguments, - w_kw_defs, freevars, w_ann, qualname=qualname) + kw_defs_w, freevars, w_ann, qualname=qualname) self.pushvalue(space.wrap(fn)) def MAKE_FUNCTION(self, oparg, next_instr): diff --git a/pypy/interpreter/signature.py b/pypy/interpreter/signature.py --- a/pypy/interpreter/signature.py +++ b/pypy/interpreter/signature.py @@ -39,6 +39,7 @@ def scope_length(self): scopelen = len(self.argnames) + scopelen += len(self.kwonlyargnames) scopelen += self.has_vararg() scopelen += self.has_kwarg() return scopelen diff --git a/pypy/interpreter/test/test_argument.py b/pypy/interpreter/test/test_argument.py --- a/pypy/interpreter/test/test_argument.py +++ b/pypy/interpreter/test/test_argument.py @@ -30,7 +30,7 @@ assert sig.num_argnames() == 3 assert sig.has_vararg() assert sig.has_kwarg() - assert sig.scope_length() == 5 + assert sig.scope_length() == 6 assert sig.getallvarnames() == ["a", "b", "c", "d", "kwonly", "c"] def test_eq(self): diff --git a/pypy/interpreter/test/test_gateway.py b/pypy/interpreter/test/test_gateway.py --- a/pypy/interpreter/test/test_gateway.py +++ b/pypy/interpreter/test/test_gateway.py @@ -47,6 +47,12 @@ code = gateway.BuiltinCode(f, unwrap_spec=[gateway.ObjSpace, "index"]) assert code.signature() == Signature(["index"], None, None) + def f(space, __kwonly__, w_x): + pass + code = gateway.BuiltinCode(f, unwrap_spec=[gateway.ObjSpace, + "kwonly", W_Root]) + assert code.signature() == Signature([], kwonlyargnames=['x']) + def test_call(self): def c(space, w_x, w_y, hello_w): @@ -753,7 +759,7 @@ @gateway.unwrap_spec(w_x = WrappedDefault(42), y=int) def g(space, w_x, y): never_called - py.test.raises(AssertionError, space.wrap, gateway.interp2app_temp(g)) + py.test.raises(KeyError, space.wrap, gateway.interp2app_temp(g)) def test_unwrap_spec_default_applevel_bug2(self): space = self.space @@ -803,6 +809,80 @@ w_res = space.call_args(w_g, args) assert space.eq_w(w_res, space.newbytes('foo')) + def test_unwrap_spec_kwonly(self): + space = self.space + def g(space, w_x, __kwonly__, w_y): + return space.sub(w_x, w_y) + w_g = space.wrap(gateway.interp2app_temp(g)) + w = space.wrap + w1 = w(1) + + for i in range(4): + a = argument.Arguments(space, [w1, w1, w1]) + py.test.raises(gateway.OperationError, space.call_args, w_g, a) + py.test.raises(gateway.OperationError, space.call_function, w_g, + *(i * (w1,))) + + args = argument.Arguments(space, [w(1)], + w_starstararg = w({'y': 10})) + assert space.eq_w(space.call_args(w_g, args), w(-9)) + args = argument.Arguments(space, [], + w_starstararg = w({'x': 2, 'y': 10})) + assert space.eq_w(space.call_args(w_g, args), w(-8)) + + def test_unwrap_spec_kwonly_default(self): + space = self.space + @gateway.unwrap_spec(w_x2=WrappedDefault(50), y2=int) + def g(space, w_x1, w_x2, __kwonly__, w_y1, y2=200): + return space.sub(space.sub(w_x1, w_x2), + space.sub(w_y1, w(y2))) + w_g = space.wrap(gateway.interp2app_temp(g)) + w = space.wrap + w1 = w(1) + + for i in range(6): + py.test.raises(gateway.OperationError, space.call_function, w_g, + *(i * (w1,))) + + def expected(x1, x2=50, y1="missing", y2=200): + return (x1 - x2) - (y1 - y2) + + def check(*args, **kwds): + a = argument.Arguments(space, [], w_stararg = w(args), + w_starstararg = w(kwds)) + w_res = space.call_args(w_g, a) + assert space.eq_w(w_res, w(expected(*args, **kwds))) + + del kwds['y1'] + a = argument.Arguments(space, [], w_stararg = w(args), + w_starstararg = w(kwds)) + py.test.raises(gateway.OperationError, space.call_args, w_g, a) + + args += (1234,) + a = argument.Arguments(space, [], w_stararg = w(args), + w_starstararg = w(kwds)) + py.test.raises(gateway.OperationError, space.call_args, w_g, a) + + check(5, y1=1234) + check(5, 1, y1=1234) + check(5, x2=1, y1=1234) + check(5, y1=1234, y2=343) + check(5, 1, y1=1234, y2=343) + check(5, x2=1, y1=1234, y2=343) + check(x1=5, y1=1234, ) + check(x1=5, x2=1, y1=1234, ) + check(x1=5, y1=1234, y2=343) + check(x1=5, x2=1, y1=1234, y2=343) + + def test_unwrap_spec_kwonly_default_2(self): + space = self.space + @gateway.unwrap_spec(w_x2=WrappedDefault(50)) + def g(space, w_x2=None): + return w_x2 + w_g = space.wrap(gateway.interp2app_temp(g)) + w_res = space.call_function(w_g) + assert space.eq_w(w_res, space.wrap(50)) + class AppTestPyTestMark: @py.test.mark.unlikely_to_exist diff --git a/pypy/interpreter/test/test_interpreter.py b/pypy/interpreter/test/test_interpreter.py --- a/pypy/interpreter/test/test_interpreter.py +++ b/pypy/interpreter/test/test_interpreter.py @@ -435,6 +435,20 @@ assert X().f() == 42 """ + def test_kwonlyarg_required(self): + """ + def f(*, a=5, b): + return (a, b) + assert f(b=10) == (5, 10) + assert f(a=7, b=12) == (7, 12) + raises(TypeError, f) + raises(TypeError, f, 1) + raises(TypeError, f, 1, 1) + raises(TypeError, f, a=1) + raises(TypeError, f, 1, a=1) + raises(TypeError, f, 1, b=1) + """ + def test_extended_unpacking_short(self): """ class Seq: diff --git a/pypy/module/__pypy__/interp_magic.py b/pypy/module/__pypy__/interp_magic.py --- a/pypy/module/__pypy__/interp_magic.py +++ b/pypy/module/__pypy__/interp_magic.py @@ -131,7 +131,7 @@ @unwrap_spec(w_module=MixedModule) def save_module_content_for_future_reload(space, w_module): - w_module.save_module_content_for_future_reload(save_all=True) + w_module.save_module_content_for_future_reload() def set_code_callback(space, w_callable): cache = space.fromcache(CodeHookCache) diff --git a/pypy/module/_frozen_importlib/__init__.py b/pypy/module/_frozen_importlib/__init__.py --- a/pypy/module/_frozen_importlib/__init__.py +++ b/pypy/module/_frozen_importlib/__init__.py @@ -16,11 +16,10 @@ @staticmethod def _compile_bootstrap_module(space, name, w_name, w_dict): """NOT_RPYTHON""" - ec = space.getexecutioncontext() with open(os.path.join(lib_python, 'importlib', name + '.py')) as fp: source = fp.read() pathname = "<frozen importlib.%s>" % name - code_w = ec.compiler.compile(source, pathname, 'exec', 0) + code_w = Module._cached_compile(space, source, pathname, 'exec', 0) space.setitem(w_dict, space.wrap('__name__'), w_name) space.setitem(w_dict, space.wrap('__builtins__'), space.wrap(space.builtin)) @@ -43,6 +42,31 @@ self.w_import = space.wrap(interp_import.import_with_frames_removed) + @staticmethod + def _cached_compile(space, source, *args): + from rpython.config.translationoption import CACHE_DIR + from pypy.module.marshal import interp_marshal + + cachename = os.path.join(CACHE_DIR, 'frozen_importlib_bootstrap') + try: + if space.config.translating: + raise IOError("don't use the cache when translating pypy") + with open(cachename, 'rb') as f: + previous = f.read(len(source) + 1) + if previous != source + '\x00': + raise IOError("source changed") + w_bin = space.newbytes(f.read()) + code_w = interp_marshal.loads(space, w_bin) + except IOError: + # must (re)compile the source + ec = space.getexecutioncontext() + code_w = ec.compiler.compile(source, *args) + w_bin = interp_marshal.dumps(space, code_w, space.wrap(2)) + content = source + '\x00' + space.bytes_w(w_bin) + with open(cachename, 'wb') as f: + f.write(content) + return code_w + def startup(self, space): """Copy our __import__ to builtins.""" w_install = self.getdictvalue(space, '_install') diff --git a/pypy/module/_pypyjson/interp_decoder.py b/pypy/module/_pypyjson/interp_decoder.py --- a/pypy/module/_pypyjson/interp_decoder.py +++ b/pypy/module/_pypyjson/interp_decoder.py @@ -327,7 +327,8 @@ i += 1 if ch == '"': content_utf8 = builder.build() - content_unicode = unicodehelper.decode_utf8(self.space, content_utf8) + content_unicode = unicodehelper.decode_utf8( + self.space, content_utf8, allow_surrogates=True) self.last_type = TYPE_STRING self.pos = i return self.space.wrap(content_unicode) @@ -374,7 +375,8 @@ # this point # uchr = runicode.code_to_unichr(val) # may be a surrogate pair again - utf8_ch = unicodehelper.encode_utf8(self.space, uchr) + utf8_ch = unicodehelper.encode_utf8( + self.space, uchr, allow_surrogates=True) builder.append(utf8_ch) return i diff --git a/pypy/module/_pypyjson/test/test__pypyjson.py b/pypy/module/_pypyjson/test/test__pypyjson.py --- a/pypy/module/_pypyjson/test/test__pypyjson.py +++ b/pypy/module/_pypyjson/test/test__pypyjson.py @@ -10,10 +10,10 @@ assert dec.skip_whitespace(8) == len(s) dec.close() - + class AppTest(object): - spaceconfig = {"objspace.usemodules._pypyjson": True} + spaceconfig = {"usemodules": ['_pypyjson']} def test_raise_on_bytes(self): import _pypyjson @@ -40,7 +40,7 @@ raises(ValueError, _pypyjson.loads, 'fa') raises(ValueError, _pypyjson.loads, 'f') raises(ValueError, _pypyjson.loads, 'falXX') - + def test_decode_string(self): import _pypyjson @@ -69,7 +69,7 @@ import _pypyjson assert _pypyjson.loads(r'"\\"') == '\\' assert _pypyjson.loads(r'"\""') == '"' - assert _pypyjson.loads(r'"\/"') == '/' + assert _pypyjson.loads(r'"\/"') == '/' assert _pypyjson.loads(r'"\b"') == '\b' assert _pypyjson.loads(r'"\f"') == '\f' assert _pypyjson.loads(r'"\n"') == '\n' @@ -85,7 +85,7 @@ import _pypyjson s = r'"hello\nworld' # missing the trailing " raises(ValueError, "_pypyjson.loads(s)") - + def test_escape_sequence_unicode(self): import _pypyjson s = r'"\u1234"' @@ -166,7 +166,7 @@ def test_decode_object_nonstring_key(self): import _pypyjson raises(ValueError, "_pypyjson.loads('{42: 43}')") - + def test_decode_array(self): import _pypyjson assert _pypyjson.loads('[]') == [] @@ -183,7 +183,7 @@ res = _pypyjson.loads('"z\\ud834\\udd20x"') assert res == expected - def test_surrogate_pair(self): + def test_lone_surrogate(self): import _pypyjson json = '{"a":"\\uD83D"}' res = _pypyjson.loads(json) diff --git a/pypy/module/posix/interp_posix.py b/pypy/module/posix/interp_posix.py --- a/pypy/module/posix/interp_posix.py +++ b/pypy/module/posix/interp_posix.py @@ -15,8 +15,7 @@ from rpython.rlib.unroll import unrolling_iterable from rpython.tool.sourcetools import func_with_new_name -from pypy.interpreter.gateway import ( - unwrap_spec, WrappedDefault, Unwrapper, kwonly) +from pypy.interpreter.gateway import unwrap_spec, WrappedDefault, Unwrapper from pypy.interpreter.error import ( OperationError, oefmt, wrap_oserror, wrap_oserror2, strerror as _strerror) from pypy.interpreter.executioncontext import ExecutionContext @@ -211,7 +210,8 @@ "%s: %s unavailable on this platform", funcname, arg) @unwrap_spec(flags=c_int, mode=c_int, dir_fd=DirFD(rposix.HAVE_OPENAT)) -def open(space, w_path, flags, mode=0777, dir_fd=DEFAULT_DIR_FD): +def open(space, w_path, flags, mode=0777, + __kwonly__=None, dir_fd=DEFAULT_DIR_FD): """open(path, flags, mode=0o777, *, dir_fd=None) Open a file for low level IO. Returns a file handle (integer). @@ -428,8 +428,8 @@ @unwrap_spec( path=path_or_fd(allow_fd=True), dir_fd=DirFD(rposix.HAVE_FSTATAT), - follow_symlinks=kwonly(bool)) -def stat(space, path, dir_fd=DEFAULT_DIR_FD, follow_symlinks=True): + follow_symlinks=bool) +def stat(space, path, __kwonly__, dir_fd=DEFAULT_DIR_FD, follow_symlinks=True): """stat(path, *, dir_fd=None, follow_symlinks=True) -> stat result Perform a stat system call on the given path. @@ -476,7 +476,7 @@ @unwrap_spec( path=path_or_fd(allow_fd=False), dir_fd=DirFD(rposix.HAVE_FSTATAT)) -def lstat(space, path, dir_fd=DEFAULT_DIR_FD): +def lstat(space, path, __kwonly__, dir_fd=DEFAULT_DIR_FD): """lstat(path, *, dir_fd=None) -> stat result Like stat(), but do not follow symbolic links. @@ -551,9 +551,9 @@ raise wrap_oserror(space, e) @unwrap_spec(mode=c_int, - dir_fd=DirFD(rposix.HAVE_FACCESSAT), effective_ids=kwonly(bool), - follow_symlinks=kwonly(bool)) -def access(space, w_path, mode, + dir_fd=DirFD(rposix.HAVE_FACCESSAT), effective_ids=bool, + follow_symlinks=bool) +def access(space, w_path, mode, __kwonly__, dir_fd=DEFAULT_DIR_FD, effective_ids=False, follow_symlinks=True): """\ access(path, mode, *, dir_fd=None, effective_ids=False, follow_symlinks=True) @@ -626,7 +626,7 @@ return space.wrap(rc) @unwrap_spec(dir_fd=DirFD(rposix.HAVE_UNLINKAT)) -def unlink(space, w_path, dir_fd=DEFAULT_DIR_FD): +def unlink(space, w_path, __kwonly__, dir_fd=DEFAULT_DIR_FD): """unlink(path, *, dir_fd=None) Remove a file (same as remove()). @@ -645,7 +645,7 @@ raise wrap_oserror2(space, e, w_path) @unwrap_spec(dir_fd=DirFD(rposix.HAVE_UNLINKAT)) -def remove(space, w_path, dir_fd=DEFAULT_DIR_FD): +def remove(space, w_path, __kwonly__, dir_fd=DEFAULT_DIR_FD): """remove(path, *, dir_fd=None) Remove a file (same as unlink()). @@ -710,7 +710,7 @@ raise wrap_oserror2(space, e, w_path) @unwrap_spec(mode=c_int, dir_fd=DirFD(rposix.HAVE_MKDIRAT)) -def mkdir(space, w_path, mode=0o777, dir_fd=DEFAULT_DIR_FD): +def mkdir(space, w_path, mode=0o777, __kwonly__=None, dir_fd=DEFAULT_DIR_FD): """mkdir(path, mode=0o777, *, dir_fd=None) Create a directory. @@ -731,7 +731,7 @@ raise wrap_oserror2(space, e, w_path) @unwrap_spec(dir_fd=DirFD(rposix.HAVE_UNLINKAT)) -def rmdir(space, w_path, dir_fd=DEFAULT_DIR_FD): +def rmdir(space, w_path, __kwonly__, dir_fd=DEFAULT_DIR_FD): """rmdir(path, *, dir_fd=None) Remove a directory. @@ -901,8 +901,9 @@ return space.newtuple([space.wrap(fd1), space.wrap(fd2)]) @unwrap_spec(mode=c_int, dir_fd=DirFD(rposix.HAVE_FCHMODAT), - follow_symlinks=kwonly(bool)) -def chmod(space, w_path, mode, dir_fd=DEFAULT_DIR_FD, follow_symlinks=True): + follow_symlinks=bool) +def chmod(space, w_path, mode, __kwonly__, + dir_fd=DEFAULT_DIR_FD, follow_symlinks=True): """chmod(path, mode, *, dir_fd=None, follow_symlinks=True) Change the access permissions of a file. @@ -968,7 +969,7 @@ @unwrap_spec(src_dir_fd=DirFD(rposix.HAVE_RENAMEAT), dst_dir_fd=DirFD(rposix.HAVE_RENAMEAT)) -def rename(space, w_src, w_dst, +def rename(space, w_src, w_dst, __kwonly__, src_dir_fd=DEFAULT_DIR_FD, dst_dir_fd=DEFAULT_DIR_FD): """rename(src, dst, *, src_dir_fd=None, dst_dir_fd=None) @@ -992,7 +993,7 @@ @unwrap_spec(src_dir_fd=DirFD(rposix.HAVE_RENAMEAT), dst_dir_fd=DirFD(rposix.HAVE_RENAMEAT)) -def replace(space, w_src, w_dst, +def replace(space, w_src, w_dst, __kwonly__, src_dir_fd=DEFAULT_DIR_FD, dst_dir_fd=DEFAULT_DIR_FD): """replace(src, dst, *, src_dir_fd=None, dst_dir_fd=None) @@ -1015,7 +1016,7 @@ raise wrap_oserror(space, e) @unwrap_spec(mode=c_int, dir_fd=DirFD(rposix.HAVE_MKFIFOAT)) -def mkfifo(space, w_path, mode=0666, dir_fd=DEFAULT_DIR_FD): +def mkfifo(space, w_path, mode=0666, __kwonly__=None, dir_fd=DEFAULT_DIR_FD): """mkfifo(path, mode=0o666, *, dir_fd=None) Create a FIFO (a POSIX named pipe). @@ -1034,7 +1035,8 @@ raise wrap_oserror2(space, e, w_path) @unwrap_spec(mode=c_int, device=c_int, dir_fd=DirFD(rposix.HAVE_MKNODAT)) -def mknod(space, w_filename, mode=0600, device=0, dir_fd=DEFAULT_DIR_FD): +def mknod(space, w_filename, mode=0600, device=0, + __kwonly__=None, dir_fd=DEFAULT_DIR_FD): """mknod(filename, mode=0o600, device=0, *, dir_fd=None) Create a filesystem node (file, device special file or named pipe) @@ -1096,9 +1098,9 @@ @unwrap_spec( src='fsencode', dst='fsencode', src_dir_fd=DirFD(rposix.HAVE_LINKAT), dst_dir_fd=DirFD(rposix.HAVE_LINKAT), - follow_symlinks=kwonly(bool)) + follow_symlinks=bool) def link( - space, src, dst, + space, src, dst, __kwonly__, src_dir_fd=DEFAULT_DIR_FD, dst_dir_fd=DEFAULT_DIR_FD, follow_symlinks=True): """\ @@ -1128,7 +1130,7 @@ @unwrap_spec(dir_fd=DirFD(rposix.HAVE_SYMLINKAT)) def symlink(space, w_src, w_dst, w_target_is_directory=None, - dir_fd=DEFAULT_DIR_FD): + __kwonly__=None, dir_fd=DEFAULT_DIR_FD): """symlink(src, dst, target_is_directory=False, *, dir_fd=None) Create a symbolic link pointing to src named dst. @@ -1156,7 +1158,7 @@ @unwrap_spec( path=path_or_fd(allow_fd=False), dir_fd=DirFD(rposix.HAVE_READLINKAT)) -def readlink(space, path, dir_fd=DEFAULT_DIR_FD): +def readlink(space, path, __kwonly__, dir_fd=DEFAULT_DIR_FD): """readlink(path, *, dir_fd=None) -> path Return a string representing the path to which the symbolic link points. @@ -1356,9 +1358,9 @@ @unwrap_spec( path=path_or_fd(allow_fd=rposix.HAVE_FUTIMENS or rposix.HAVE_FUTIMES), - w_times=WrappedDefault(None), w_ns=kwonly(WrappedDefault(None)), - dir_fd=DirFD(rposix.HAVE_UTIMENSAT), follow_symlinks=kwonly(bool)) -def utime(space, path, w_times, w_ns, dir_fd=DEFAULT_DIR_FD, + w_times=WrappedDefault(None), w_ns=WrappedDefault(None), + dir_fd=DirFD(rposix.HAVE_UTIMENSAT), follow_symlinks=bool) +def utime(space, path, w_times, __kwonly__, w_ns, dir_fd=DEFAULT_DIR_FD, follow_symlinks=True): """utime(path, times=None, *, ns=None, dir_fd=None, follow_symlinks=True) @@ -1892,8 +1894,9 @@ @unwrap_spec( uid=c_uid_t, gid=c_gid_t, - dir_fd=DirFD(rposix.HAVE_FCHOWNAT), follow_symlinks=kwonly(bool)) -def chown(space, w_path, uid, gid, dir_fd=DEFAULT_DIR_FD, follow_symlinks=True): + dir_fd=DirFD(rposix.HAVE_FCHOWNAT), follow_symlinks=bool) +def chown(space, w_path, uid, gid, __kwonly__, + dir_fd=DEFAULT_DIR_FD, follow_symlinks=True): """chown(path, uid, gid, *, dir_fd=None, follow_symlinks=True) Change the owner and group id of path to the numeric uid and gid. diff --git a/pypy/module/sys/__init__.py b/pypy/module/sys/__init__.py --- a/pypy/module/sys/__init__.py +++ b/pypy/module/sys/__init__.py @@ -14,6 +14,13 @@ """NOT_RPYTHON""" # because parent __init__ isn't if space.config.translating: del self.__class__.interpleveldefs['pypy_getudir'] + del self.__class__.appleveldefs['stdin'] + del self.__class__.appleveldefs['__stdin__'] + del self.__class__.appleveldefs['stdout'] + del self.__class__.appleveldefs['__stdout__'] + del self.__class__.appleveldefs['stderr'] + del self.__class__.appleveldefs['__stderr__'] + super(Module, self).__init__(space, w_name) self.recursionlimit = 100 self.defaultencoding = "utf-8" @@ -99,6 +106,15 @@ 'flags' : 'app.null_sysflags', '_xoptions' : 'app.null__xoptions', 'implementation' : 'app.implementation', + + # these six attributes are here only during tests; + # they are removed before translation + 'stdin' : 'std_test.stdin', + '__stdin__' : 'std_test.stdin', + 'stdout' : 'std_test.stdout', + '__stdout__' : 'std_test.stdout', + 'stderr' : 'std_test.stderr', + '__stderr__' : 'std_test.stderr', } def startup(self, space): @@ -123,28 +139,12 @@ space = self.space if not space.config.translating: - from pypy.module.sys.interp_encoding import _getfilesystemencoding - self.filesystemencoding = _getfilesystemencoding(space) - - if not space.config.translating: - # Install standard streams for tests that don't call app_main. - # Always use line buffering, even for tests that capture - # standard descriptors. - space.appexec([], """(): - import sys, io - sys.stdin = sys.__stdin__ = io.open(0, "r", encoding="ascii", - closefd=False) - sys.stdin.buffer.raw.name = "<stdin>" - sys.stdout = sys.__stdout__ = io.open(1, "w", encoding="ascii", - buffering=1, - closefd=False) - sys.stdout.buffer.raw.name = "<stdout>" - sys.stderr = sys.__stderr__ = io.open(2, "w", encoding="ascii", - errors="backslashreplace", - buffering=1, - closefd=False) - sys.stderr.buffer.raw.name = "<stderr>" - """) + ##from pypy.module.sys.interp_encoding import _getfilesystemencoding + ##self.filesystemencoding = _getfilesystemencoding(space) + # XXX the two lines above take a few seconds to run whenever + # we initialize the space; for tests, use a simpler version + from pypy.module.sys.interp_encoding import base_encoding + self.filesystemencoding = space.wrap(base_encoding) def flush_std_files(self, space): w_stdout = space.sys.getdictvalue(space, 'stdout') diff --git a/pypy/module/sys/std_test.py b/pypy/module/sys/std_test.py new file mode 100644 --- /dev/null +++ b/pypy/module/sys/std_test.py @@ -0,0 +1,19 @@ +# Install standard streams for tests that don't call app_main. Always +# use line buffering, even for tests that capture standard descriptors. + +import io + +stdin = io.open(0, "r", encoding="ascii", + closefd=False) +stdin.buffer.raw.name = "<stdin>" + +stdout = io.open(1, "w", encoding="ascii", + buffering=1, + closefd=False) +stdout.buffer.raw.name = "<stdout>" + +stderr = io.open(2, "w", encoding="ascii", + errors="backslashreplace", + buffering=1, + closefd=False) +stderr.buffer.raw.name = "<stderr>" diff --git a/pypy/objspace/std/mapdict.py b/pypy/objspace/std/mapdict.py --- a/pypy/objspace/std/mapdict.py +++ b/pypy/objspace/std/mapdict.py @@ -68,6 +68,7 @@ @jit.elidable def find_map_attr(self, name, index): # attr cache + assert type(name) is str # utf8-encoded space = self.space cache = space.fromcache(MapAttrCache) SHIFT2 = r_uint.BITS - space.config.objspace.std.methodcachesizeexp @@ -336,7 +337,7 @@ space = self.space w_dict = obj.getdict(space) try: - space.delitem(w_dict, space.wrap(name)) + space.delitem(w_dict, space.wrap(name.decode('utf-8'))) except OperationError as ex: if not ex.match(space, space.w_KeyError): raise @@ -401,7 +402,7 @@ def materialize_r_dict(self, space, obj, dict_w): new_obj = self.back.materialize_r_dict(space, obj, dict_w) if self.index == DICT: - w_attr = space.wrap(self.name) + w_attr = space.wrap(self.name.decode('utf-8')) dict_w[w_attr] = obj._mapdict_read_storage(self.storageindex) else: self._copy_attr(obj, new_obj) @@ -809,7 +810,7 @@ raise KeyError key = curr.name w_value = self.getitem_str(w_dict, key) - w_key = self.space.wrap(key) + w_key = self.space.wrap(key.decode('utf-8')) self.delitem(w_dict, w_key) return (w_key, w_value) @@ -844,7 +845,7 @@ if curr_map: self.curr_map = curr_map.back attr = curr_map.name - w_attr = self.space.wrap(attr) + w_attr = self.space.wrap(attr.decode('utf-8')) return w_attr return None @@ -885,7 +886,7 @@ if curr_map: self.curr_map = curr_map.back attr = curr_map.name - w_attr = self.space.wrap(attr) + w_attr = self.space.wrap(attr.decode('utf-8')) return w_attr, self.w_obj.getdictvalue(self.space, attr) return None, None diff --git a/pypy/objspace/std/test/test_dictmultiobject.py b/pypy/objspace/std/test/test_dictmultiobject.py --- a/pypy/objspace/std/test/test_dictmultiobject.py +++ b/pypy/objspace/std/test/test_dictmultiobject.py @@ -1282,7 +1282,7 @@ assert a == self.string2 assert b == 2000 if not self._str_devolves: - result = self.impl.getitem_str(self.string) + result = self.impl.getitem_str(self.string.encode('utf-8')) else: result = self.impl.getitem(self.string) assert result == 1000 @@ -1293,7 +1293,7 @@ assert self.impl.length() == 1 assert self.impl.getitem(self.string) == 1000 if not self._str_devolves: - result = self.impl.getitem_str(self.string) + result = self.impl.getitem_str(self.string.encode('utf-8')) else: result = self.impl.getitem(self.string) assert result == 1000 diff --git a/pypy/objspace/std/test/test_mapdict.py b/pypy/objspace/std/test/test_mapdict.py --- a/pypy/objspace/std/test/test_mapdict.py +++ b/pypy/objspace/std/test/test_mapdict.py @@ -1,3 +1,5 @@ +# -*- coding: utf-8 -*- + from pypy.objspace.std.test.test_dictmultiobject import FakeSpace, W_DictObject from pypy.objspace.std.mapdict import * @@ -873,6 +875,15 @@ d = x.__dict__ assert list(__pypy__.reversed_dict(d)) == list(d.keys())[::-1] + def test_nonascii_argname(self): + """ + class X: + pass + x = X() + x.日本 = 3 + assert x.日本 == 3 + assert x.__dict__ == {'日本': 3} + """ class AppTestWithMapDictAndCounters(object): spaceconfig = {"objspace.std.withmethodcachecounter": True} _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit