[pypy-commit] pypy py3k: (antocuni, romain): implement keyword-only arguments in the astcompiler and in the interpreter
antocuni
noreply at buildbot.pypy.org
Thu Jan 19 23:20:22 CET 2012
Author: Antonio Cuni <anto.cuni at gmail.com>
Branch: py3k
Changeset: r51503:243e484588fb
Date: 2012-01-19 23:20 +0100
http://bitbucket.org/pypy/pypy/changeset/243e484588fb/
Log: (antocuni, romain): implement keyword-only arguments in the
astcompiler and in the interpreter
diff --git a/pypy/interpreter/argument.py b/pypy/interpreter/argument.py
--- a/pypy/interpreter/argument.py
+++ b/pypy/interpreter/argument.py
@@ -10,19 +10,27 @@
class Signature(object):
_immutable_ = True
_immutable_fields_ = ["argnames[*]"]
- __slots__ = ("argnames", "varargname", "kwargname")
+ __slots__ = ("argnames", "kwonlyargnames", "varargname", "kwargname")
- def __init__(self, argnames, varargname=None, kwargname=None):
+ def __init__(self, argnames, varargname=None, kwargname=None, kwonlyargnames=None):
self.argnames = argnames
self.varargname = varargname
self.kwargname = kwargname
+ if kwonlyargnames is None:
+ kwonlyargnames = []
+ self.kwonlyargnames = kwonlyargnames
@jit.elidable
def find_argname(self, name):
try:
return self.argnames.index(name)
except ValueError:
- return -1
+ pass
+ try:
+ return len(self.argnames) + self.kwonlyargnames.index(name)
+ except ValueError:
+ pass
+ return -1
def num_argnames(self):
return len(self.argnames)
@@ -43,20 +51,22 @@
argnames = self.argnames
if self.varargname is not None:
argnames = argnames + [self.varargname]
+ argnames += self.kwonlyargnames
if self.kwargname is not None:
argnames = argnames + [self.kwargname]
return argnames
def __repr__(self):
- return "Signature(%r, %r, %r)" % (
- self.argnames, self.varargname, self.kwargname)
+ return "Signature(%r, %r, %r, %r)" % (
+ self.argnames, self.varargname, self.kwargname, self.kwonlyargnames)
def __eq__(self, other):
if not isinstance(other, Signature):
return NotImplemented
return (self.argnames == other.argnames and
self.varargname == other.varargname and
- self.kwargname == other.kwargname)
+ self.kwargname == other.kwargname and
+ self.kwonlyargnames == other.kwonlyargnames)
def __ne__(self, other):
if not isinstance(other, Signature):
diff --git a/pypy/interpreter/astcompiler/assemble.py b/pypy/interpreter/astcompiler/assemble.py
--- a/pypy/interpreter/astcompiler/assemble.py
+++ b/pypy/interpreter/astcompiler/assemble.py
@@ -147,6 +147,7 @@
self.free_vars = _list_to_dict(scope.free_vars, len(self.cell_vars))
self.w_consts = space.newdict()
self.argcount = 0
+ self.kwonlyargcount = 0
self.lineno_set = False
self.lineno = 0
self.add_none_to_final_return = True
@@ -459,6 +460,7 @@
bytecode = ''.join([block.get_code() for block in blocks])
return pycode.PyCode(self.space,
self.argcount,
+ self.kwonlyargcount,
len(self.var_names),
stack_depth,
flags,
diff --git a/pypy/interpreter/astcompiler/codegen.py b/pypy/interpreter/astcompiler/codegen.py
--- a/pypy/interpreter/astcompiler/codegen.py
+++ b/pypy/interpreter/astcompiler/codegen.py
@@ -1173,6 +1173,9 @@
if args.args:
self._handle_nested_args(args.args)
self.argcount = len(args.args)
+ if args.kwonlyargs:
+ self._handle_nested_args(args.kwonlyargs)
+ self.kwonlyargcount = len(args.kwonlyargs)
if func.body:
for i in range(start, len(func.body)):
func.body[i].walkabout(self)
diff --git a/pypy/interpreter/astcompiler/symtable.py b/pypy/interpreter/astcompiler/symtable.py
--- a/pypy/interpreter/astcompiler/symtable.py
+++ b/pypy/interpreter/astcompiler/symtable.py
@@ -495,6 +495,8 @@
assert isinstance(scope, FunctionScope) # Annotator hint.
if arguments.args:
self._handle_params(arguments.args, True)
+ if arguments.kwonlyargs:
+ self._handle_params(arguments.kwonlyargs, True)
if arguments.vararg:
self.note_symbol(arguments.vararg, SYM_PARAM)
scope.note_variable_arg(arguments.vararg)
diff --git a/pypy/interpreter/astcompiler/test/test_compiler.py b/pypy/interpreter/astcompiler/test/test_compiler.py
--- a/pypy/interpreter/astcompiler/test/test_compiler.py
+++ b/pypy/interpreter/astcompiler/test/test_compiler.py
@@ -5,6 +5,7 @@
from pypy.interpreter.pyparser.test import expressions
from pypy.interpreter.pycode import PyCode
from pypy.interpreter.pyparser.error import SyntaxError, IndentationError
+from pypy.interpreter.error import OperationError
from pypy.tool import stdlib_opcode as ops
def compile_with_astcompiler(expr, mode, space):
@@ -41,9 +42,11 @@
source = str(py.code.Source(source))
space = self.space
code = compile_with_astcompiler(source, 'exec', space)
- # 2.7 bytecode is too different, the standard `dis` module crashes
+ # 3.2 bytecode is too different, the standard `dis` module crashes
# on older cpython versions
- if sys.version_info >= (2, 7):
+ if sys.version_info >= (3, 2):
+ # this will only (maybe) work in the far future, when we run pypy
+ # on top of Python 3. For now, it's just disabled
print
code.dump()
w_dict = space.newdict()
@@ -242,7 +245,9 @@
return a, b
""")
decl = str(decl) + '\n'
- yield self.st, decl + "x=f(1, b=2)", "x", (1, 2)
+ self.st(decl + "x=f(1, b=2)", "x", (1, 2))
+ operr = py.test.raises(OperationError, 'self.st(decl + "x=f(1, 2)", "x", (1, 2))')
+ assert operr.value.w_type is self.space.w_TypeError
def test_listmakers(self):
yield (self.st,
diff --git a/pypy/interpreter/astcompiler/test/test_symtable.py b/pypy/interpreter/astcompiler/test/test_symtable.py
--- a/pypy/interpreter/astcompiler/test/test_symtable.py
+++ b/pypy/interpreter/astcompiler/test/test_symtable.py
@@ -131,6 +131,11 @@
for name in ("a", "b", "c"):
assert scp.lookup(name) == symtable.SCOPE_LOCAL
+ def test_arguments_kwonly(self):
+ scp = self.func_scope("def f(a, *, b): pass")
+ for name in ("a", "b"):
+ assert scp.lookup(name) == symtable.SCOPE_LOCAL
+
def test_function(self):
scp = self.func_scope("def f(): x = 4")
assert scp.lookup("x") == symtable.SCOPE_LOCAL
diff --git a/pypy/interpreter/pycode.py b/pypy/interpreter/pycode.py
--- a/pypy/interpreter/pycode.py
+++ b/pypy/interpreter/pycode.py
@@ -16,7 +16,7 @@
from pypy.rlib.rarithmetic import intmask
from pypy.rlib.debug import make_sure_not_resized
from pypy.rlib import jit
-from pypy.rlib.objectmodel import compute_hash
+from pypy.rlib.objectmodel import compute_hash, we_are_translated
from pypy.tool.stdlib_opcode import opcodedesc, HAVE_ARGUMENT
# helper
@@ -42,8 +42,16 @@
def cpython_code_signature(code):
"([list-of-arg-names], vararg-name-or-None, kwarg-name-or-None)."
argcount = code.co_argcount
+ if we_are_translated():
+ kwonlyargcount = code.co_kwonlyargcount
+ else:
+ # for compatibility with CPython 2.7 code objects
+ kwonlyargcount = getattr(code, 'co_kwonlyargcount', 0)
assert argcount >= 0 # annotator hint
+ assert kwonlyargcount >= 0
argnames = list(code.co_varnames[:argcount])
+ n = argcount + kwonlyargcount
+ kwonlyargs = list(code.co_varnames[argcount:n])
if code.co_flags & CO_VARARGS:
varargname = code.co_varnames[argcount]
argcount += 1
@@ -54,7 +62,7 @@
argcount += 1
else:
kwargname = None
- return Signature(argnames, varargname, kwargname)
+ return Signature(argnames, varargname, kwargname, kwonlyargs)
class PyCode(eval.Code):
"CPython-style code objects."
@@ -62,7 +70,7 @@
_immutable_fields_ = ["co_consts_w[*]", "co_names_w[*]", "co_varnames[*]",
"co_freevars[*]", "co_cellvars[*]"]
- def __init__(self, space, argcount, nlocals, stacksize, flags,
+ def __init__(self, space, argcount, kwonlyargcount, nlocals, stacksize, flags,
code, consts, names, varnames, filename,
name, firstlineno, lnotab, freevars, cellvars,
hidden_applevel=False, magic = default_magic):
@@ -72,6 +80,7 @@
eval.Code.__init__(self, name)
assert nlocals >= 0
self.co_argcount = argcount
+ self.co_kwonlyargcount = kwonlyargcount
self.co_nlocals = nlocals
self.co_stacksize = stacksize
self.co_flags = flags
@@ -175,6 +184,7 @@
# stick the underlying CPython magic value, if the code object
# comes from there
return cls(space, code.co_argcount,
+ getattr(code, 'co_kwonlyargcount', 0),
code.co_nlocals,
code.co_stacksize,
code.co_flags,
@@ -250,6 +260,7 @@
consts[num] = self.space.unwrap(w)
num += 1
return new.code( self.co_argcount,
+ self.co_kwonlyargcount,
self.co_nlocals,
self.co_stacksize,
self.co_flags,
@@ -297,6 +308,7 @@
return space.w_False
areEqual = (self.co_name == other.co_name and
self.co_argcount == other.co_argcount and
+ self.co_kwonlyargcount == other.co_kwonlyargcount and
self.co_nlocals == other.co_nlocals and
self.co_flags == other.co_flags and
self.co_firstlineno == other.co_firstlineno and
@@ -323,6 +335,7 @@
space = self.space
result = compute_hash(self.co_name)
result ^= self.co_argcount
+ result ^= self.co_kwonlyargcount
result ^= self.co_nlocals
result ^= self.co_flags
result ^= self.co_firstlineno
@@ -337,12 +350,12 @@
w_result = space.xor(w_result, space.hash(w_const))
return w_result
- @unwrap_spec(argcount=int, nlocals=int, stacksize=int, flags=int,
+ @unwrap_spec(argcount=int, kwonlyargcount=int, nlocals=int, stacksize=int, flags=int,
codestring=str,
filename=str, name=str, firstlineno=int,
lnotab=str, magic=int)
def descr_code__new__(space, w_subtype,
- argcount, nlocals, stacksize, flags,
+ argcount, kwonlyargcount, nlocals, stacksize, flags,
codestring, w_constants, w_names,
w_varnames, filename, name, firstlineno,
lnotab, w_freevars=NoneNotWrapped,
@@ -351,6 +364,9 @@
if argcount < 0:
raise OperationError(space.w_ValueError,
space.wrap("code: argcount must not be negative"))
+ if kwonlyargcount < 0:
+ raise OperationError(space.w_ValueError,
+ space.wrap("code: kwonlyargcount must not be negative"))
if nlocals < 0:
raise OperationError(space.w_ValueError,
space.wrap("code: nlocals must not be negative"))
@@ -369,7 +385,7 @@
else:
cellvars = []
code = space.allocate_instance(PyCode, w_subtype)
- PyCode.__init__(code, space, argcount, nlocals, stacksize, flags, codestring, consts_w[:], names,
+ PyCode.__init__(code, space, argcount, kwonlyargcount, nlocals, stacksize, flags, codestring, consts_w[:], names,
varnames, filename, name, firstlineno, lnotab, freevars, cellvars, magic=magic)
return space.wrap(code)
@@ -381,6 +397,7 @@
w = space.wrap
tup = [
w(self.co_argcount),
+ w(self.co_kwonlyargcount),
w(self.co_nlocals),
w(self.co_stacksize),
w(self.co_flags),
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
@@ -26,12 +26,12 @@
assert sig.has_kwarg()
assert sig.scope_length() == 4
assert sig.getallvarnames() == ["a", "b", "c", "c"]
- sig = Signature(["a", "b", "c"], "d", "c")
+ sig = Signature(["a", "b", "c"], "d", "c", ["kwonly"])
assert sig.num_argnames() == 3
assert sig.has_vararg()
assert sig.has_kwarg()
assert sig.scope_length() == 5
- assert sig.getallvarnames() == ["a", "b", "c", "d", "c"]
+ assert sig.getallvarnames() == ["a", "b", "c", "d", "kwonly", "c"]
def test_eq(self):
sig1 = Signature(["a", "b", "c"], "d", "c")
@@ -40,11 +40,12 @@
def test_find_argname(self):
- sig = Signature(["a", "b", "c"], None, None)
+ sig = Signature(["a", "b", "c"], None, None, ["kwonly"])
assert sig.find_argname("a") == 0
assert sig.find_argname("b") == 1
assert sig.find_argname("c") == 2
assert sig.find_argname("d") == -1
+ assert sig.find_argname("kwonly") == 3
def test_tuply(self):
sig = Signature(["a", "b", "c"], "d", "e")
diff --git a/pypy/objspace/std/marshal_impl.py b/pypy/objspace/std/marshal_impl.py
--- a/pypy/objspace/std/marshal_impl.py
+++ b/pypy/objspace/std/marshal_impl.py
@@ -313,6 +313,7 @@
# see pypy.interpreter.pycode for the layout
x = space.interp_w(PyCode, w_pycode)
m.put_int(x.co_argcount)
+ m.put_int(x.co_kwonlyargcount)
m.put_int(x.co_nlocals)
m.put_int(x.co_stacksize)
m.put_int(x.co_flags)
@@ -355,6 +356,7 @@
def unmarshal_pycode(space, u, tc):
argcount = u.get_int()
+ kwonlyargcount = u.get_int()
nlocals = u.get_int()
stacksize = u.get_int()
flags = u.get_int()
@@ -370,7 +372,7 @@
name = unmarshal_str(u)
firstlineno = u.get_int()
lnotab = unmarshal_str(u)
- code = PyCode(space, argcount, nlocals, stacksize, flags,
+ code = PyCode(space, argcount, kwonlyargcount, nlocals, stacksize, flags,
code, consts_w[:], names, varnames, filename,
name, firstlineno, lnotab, freevars, cellvars)
return space.wrap(code)
More information about the pypy-commit
mailing list