From python-3000-checkins at python.org Sat Oct 28 01:06:02 2006 From: python-3000-checkins at python.org (guido.van.rossum) Date: Sat, 28 Oct 2006 01:06:02 +0200 (CEST) Subject: [Python-3000-checkins] r52490 - in python/branches/p3yk/Lib: modulefinder.py test/test_modulefinder.py Message-ID: <20061027230602.5CD3A1E4004@bag.python.org> Author: guido.van.rossum Date: Sat Oct 28 01:06:01 2006 New Revision: 52490 Added: python/branches/p3yk/Lib/test/test_modulefinder.py (contents, props changed) Modified: python/branches/p3yk/Lib/modulefinder.py Log: Thomas Heller fixed modulefinder and added a test. Thanks! Modified: python/branches/p3yk/Lib/modulefinder.py ============================================================================== --- python/branches/p3yk/Lib/modulefinder.py (original) +++ python/branches/p3yk/Lib/modulefinder.py Sat Oct 28 01:06:01 2006 @@ -1,13 +1,14 @@ """Find modules used by a script, using introspection.""" - # This module should be kept compatible with Python 2.2, see PEP 291. +from __future__ import generators import dis import imp import marshal import os import sys import new +import struct if hasattr(sys.__stdout__, "newlines"): READ_MODE = "U" # universal line endings @@ -15,11 +16,12 @@ # remain compatible with Python < 2.3 READ_MODE = "r" -LOAD_CONST = dis.opname.index('LOAD_CONST') -IMPORT_NAME = dis.opname.index('IMPORT_NAME') -STORE_NAME = dis.opname.index('STORE_NAME') -STORE_GLOBAL = dis.opname.index('STORE_GLOBAL') +LOAD_CONST = chr(dis.opname.index('LOAD_CONST')) +IMPORT_NAME = chr(dis.opname.index('IMPORT_NAME')) +STORE_NAME = chr(dis.opname.index('STORE_NAME')) +STORE_GLOBAL = chr(dis.opname.index('STORE_GLOBAL')) STORE_OPS = [STORE_NAME, STORE_GLOBAL] +HAVE_ARGUMENT = chr(dis.HAVE_ARGUMENT) # Modulefinder does a good job at simulating Python's, but it can not # handle __path__ modifications packages make at runtime. Therefore there @@ -118,9 +120,9 @@ stuff = (ext, "r", imp.PY_SOURCE) self.load_module(name, fp, pathname, stuff) - def import_hook(self, name, caller=None, fromlist=None): - self.msg(3, "import_hook", name, caller, fromlist) - parent = self.determine_parent(caller) + def import_hook(self, name, caller=None, fromlist=None, level=-1): + self.msg(3, "import_hook", name, caller, fromlist, level) + parent = self.determine_parent(caller, level=level) q, tail = self.find_head_package(parent, name) m = self.load_tail(q, tail) if not fromlist: @@ -129,12 +131,26 @@ self.ensure_fromlist(m, fromlist) return None - def determine_parent(self, caller): - self.msgin(4, "determine_parent", caller) - if not caller: + def determine_parent(self, caller, level=-1): + self.msgin(4, "determine_parent", caller, level) + if not caller or level == 0: self.msgout(4, "determine_parent -> None") return None pname = caller.__name__ + if level >= 1: # relative import + if caller.__path__: + level -= 1 + if level == 0: + parent = self.modules[pname] + assert parent is caller + self.msgout(4, "determine_parent ->", parent) + return parent + if pname.count(".") < level: + raise ImportError, "relative importpath too deep" + pname = ".".join(pname.split(".")[:-level]) + parent = self.modules[pname] + self.msgout(4, "determine_parent ->", parent) + return parent if caller.__path__: parent = self.modules[pname] assert caller is parent @@ -294,13 +310,13 @@ self.badmodules[name] = {} self.badmodules[name][caller.__name__] = 1 - def _safe_import_hook(self, name, caller, fromlist): + def _safe_import_hook(self, name, caller, fromlist, level=-1): # wrapper for self.import_hook() that won't raise ImportError if name in self.badmodules: self._add_badmodule(name, caller) return try: - self.import_hook(name, caller) + self.import_hook(name, caller, level=level) except ImportError, msg: self.msg(2, "ImportError:", str(msg)) self._add_badmodule(name, caller) @@ -311,38 +327,83 @@ self._add_badmodule(sub, caller) continue try: - self.import_hook(name, caller, [sub]) + self.import_hook(name, caller, [sub], level=level) except ImportError, msg: self.msg(2, "ImportError:", str(msg)) fullname = name + "." + sub self._add_badmodule(fullname, caller) + def scan_opcodes(self, co, + unpack = struct.unpack): + # Scan the code, and yield 'interesting' opcode combinations + # Version for Python 2.4 and older + code = co.co_code + names = co.co_names + consts = co.co_consts + while code: + c = code[0] + if c in STORE_OPS: + oparg, = unpack('= HAVE_ARGUMENT: + code = code[3:] + else: + code = code[1:] + + def scan_opcodes_25(self, co, + unpack = struct.unpack): + # Scan the code, and yield 'interesting' opcode combinations + # Python 2.5 version (has absolute and relative imports) + code = co.co_code + names = co.co_names + consts = co.co_consts + LOAD_LOAD_AND_IMPORT = LOAD_CONST + LOAD_CONST + IMPORT_NAME + while code: + c = code[0] + if c in STORE_OPS: + oparg, = unpack('= HAVE_ARGUMENT: + code = code[3:] + else: + code = code[1:] + def scan_code(self, co, m): code = co.co_code - n = len(code) - i = 0 - fromlist = None - while i < n: - c = code[i] - i = i+1 - op = ord(c) - if op >= dis.HAVE_ARGUMENT: - oparg = ord(code[i]) + ord(code[i+1])*256 - i = i+2 - if op == LOAD_CONST: - # An IMPORT_NAME is always preceded by a LOAD_CONST, it's - # a tuple of "from" names, or None for a regular import. - # The tuple may contain "*" for "from import *" - fromlist = co.co_consts[oparg] - elif op == IMPORT_NAME: - assert fromlist is None or type(fromlist) is tuple - name = co.co_names[oparg] + if sys.version_info >= (2, 5): + scanner = self.scan_opcodes_25 + else: + scanner = self.scan_opcodes + for what, args in scanner(co): + if what == "store": + name, = args + m.globalnames[name] = 1 + elif what == "absolute_import": + fromlist, name = args have_star = 0 if fromlist is not None: if "*" in fromlist: have_star = 1 fromlist = [f for f in fromlist if f != "*"] - self._safe_import_hook(name, m, fromlist) + self._safe_import_hook(name, m, fromlist, level=0) if have_star: # We've encountered an "import *". If it is a Python module, # the code has already been parsed and we can suck out the @@ -362,10 +423,17 @@ m.starimports[name] = 1 else: m.starimports[name] = 1 - elif op in STORE_OPS: - # keep track of all global names that are assigned to - name = co.co_names[oparg] - m.globalnames[name] = 1 + elif what == "relative_import": + level, fromlist, name = args + if name: + self._safe_import_hook(name, m, fromlist, level=level) + else: + parent = self.determine_parent(m, level=level) + self._safe_import_hook(parent.__name__, None, fromlist, level=0) + else: + # We don't expect anything else from the generator. + raise RuntimeError(what) + for c in co.co_consts: if isinstance(c, type(co)): self.scan_code(c, m) Added: python/branches/p3yk/Lib/test/test_modulefinder.py ============================================================================== --- (empty file) +++ python/branches/p3yk/Lib/test/test_modulefinder.py Sat Oct 28 01:06:01 2006 @@ -0,0 +1,259 @@ +import __future__ +import sys, os +import unittest +import distutils.dir_util +import tempfile + +from test import test_support + +try: set +except NameError: from sets import Set as set + +import modulefinder + +# Note: To test modulefinder with Python 2.2, sets.py and +# modulefinder.py must be available - they are not in the standard +# library. + +TEST_DIR = tempfile.mkdtemp() +TEST_PATH = [TEST_DIR, os.path.dirname(__future__.__file__)] + +# Each test description is a list of 5 items: +# +# 1. a module name that will be imported by modulefinder +# 2. a list of module names that modulefinder is required to find +# 3. a list of module names that modulefinder should complain +# about because they are not found +# 4. a list of module names that modulefinder should complain +# about because they MAY be not found +# 5. a string specifying packages to create; the format is obvious imo. +# +# Each package will be created in TEST_DIR, and TEST_DIR will be +# removed after the tests again. +# Modulefinder searches in a path that contains TEST_DIR, plus +# the standard Lib directory. + +maybe_test = [ + "a.module", + ["a", "a.module", "sys", + "b"], + ["c"], ["b.something"], + """\ +a/__init__.py +a/module.py + from b import something + from c import something +b/__init__.py + from sys import * +"""] + +maybe_test_new = [ + "a.module", + ["a", "a.module", "sys", + "b"], + ["c"], ["b.something"], + """\ +a/__init__.py +a/module.py + from b import something + from c import something +b/__init__.py + from sys import * +"""] + +package_test = [ + "a.module", + ["a", "a.b", "a.c", "a.module", "mymodule", "sys"], + ["blahblah", "c"], [], + """\ +mymodule.py +a/__init__.py + import blahblah + from a import b + import c +a/module.py + import sys + from a import b as x + from a.c import sillyname +a/b.py +a/c.py + from a.module import x + import mymodule as sillyname + from sys import version_info +"""] + +absolute_import_test = [ + "a.module", + ["a", "a.module", + "b", "b.x", "b.y", "b.z", + "sys", "exceptions"], + ["blahblah", "z"], [], + """\ +mymodule.py +a/__init__.py +a/module.py + import sys # sys + import blahblah # fails + import exceptions # exceptions + import b.x # b.x + from b import y # b.y + from b.z import * # b.z.* +a/exceptions.py +a/sys.py + import mymodule +a/b/__init__.py +a/b/x.py +a/b/y.py +a/b/z.py +b/__init__.py + import z +b/unused.py +b/x.py +b/y.py +b/z.py +"""] + +relative_import_test = [ + "a.module", + ["a", "a.module", + "a.b", "a.b.y", "a.b.z", + "a.b.c", "a.b.c.moduleC", + "a.b.c.d", "a.b.c.e", + "a.b.x", + "exceptions"], + [], [], + """\ +mymodule.py +a/__init__.py + from .b import y, z # a.b.y, a.b.z +a/module.py + import exceptions # exceptions +a/exceptions.py +a/sys.py +a/b/__init__.py + from ..b import x # a.b.x + #from a.b.c import moduleC + from .c import moduleC # a.b.moduleC +a/b/x.py +a/b/y.py +a/b/z.py +a/b/g.py +a/b/c/__init__.py + from ..c import e # a.b.c.e +a/b/c/moduleC.py + from ..c import d # a.b.c.d +a/b/c/d.py +a/b/c/e.py +a/b/c/x.py +"""] + +relative_import_test_2 = [ + "a.module", + ["a", "a.module", + "a.sys", + "a.b", "a.b.y", "a.b.z", + "a.b.c", "a.b.c.d", + "a.b.c.e", + "a.b.c.moduleC", + "a.b.c.f", + "a.b.x", + "a.another"], + [], [], + """\ +mymodule.py +a/__init__.py + from . import sys # a.sys +a/another.py +a/module.py + from .b import y, z # a.b.y, a.b.z +a/exceptions.py +a/sys.py +a/b/__init__.py + from .c import moduleC # a.b.c.moduleC + from .c import d # a.b.c.d +a/b/x.py +a/b/y.py +a/b/z.py +a/b/c/__init__.py + from . import e # a.b.c.e +a/b/c/moduleC.py + # + from . import f # a.b.c.f + from .. import x # a.b.x + from ... import another # a.another +a/b/c/d.py +a/b/c/e.py +a/b/c/f.py +"""] + +def open_file(path): + ##print "#", os.path.abspath(path) + dirname = os.path.dirname(path) + distutils.dir_util.mkpath(dirname) + return open(path, "w") + +def create_package(source): + ofi = None + for line in source.splitlines(): + if line.startswith(" ") or line.startswith("\t"): + ofi.write(line.strip() + "\n") + else: + ofi = open_file(os.path.join(TEST_DIR, line.strip())) + +class ModuleFinderTest(unittest.TestCase): + def _do_test(self, info, report=False): + import_this, modules, missing, maybe_missing, source = info + create_package(source) + try: + mf = modulefinder.ModuleFinder(path=TEST_PATH) + mf.import_hook(import_this) + if report: + mf.report() +## # This wouldn't work in general when executed several times: +## opath = sys.path[:] +## sys.path = TEST_PATH +## try: +## __import__(import_this) +## except: +## import traceback; traceback.print_exc() +## sys.path = opath +## return + modules = set(modules) + found = set(mf.modules.keys()) + more = list(found - modules) + less = list(modules - found) + # check if we found what we expected, not more, not less + self.failUnlessEqual((more, less), ([], [])) + + # check for missing and maybe missing modules + bad, maybe = mf.any_missing_maybe() + self.failUnlessEqual(bad, missing) + self.failUnlessEqual(maybe, maybe_missing) + finally: + distutils.dir_util.remove_tree(TEST_DIR) + + def test_package(self): + self._do_test(package_test) + + def test_maybe(self): + self._do_test(maybe_test) + + if getattr(__future__, "absolute_import", None): + + def test_maybe_new(self): + self._do_test(maybe_test_new) + + def test_absolute_imports(self): + self._do_test(absolute_import_test) + + def test_relative_imports(self): + self._do_test(relative_import_test) + + def test_relative_imports_2(self): + self._do_test(relative_import_test_2) + +def test_main(): + test_support.run_unittest(ModuleFinderTest) + +if __name__ == "__main__": + unittest.main() From python-3000-checkins at python.org Sat Oct 28 01:31:54 2006 From: python-3000-checkins at python.org (guido.van.rossum) Date: Sat, 28 Oct 2006 01:31:54 +0200 (CEST) Subject: [Python-3000-checkins] r52491 - in python/branches/p3yk: Grammar/Grammar Include/Python-ast.h Include/code.h Include/eval.h Include/funcobject.h Lib/compiler/ast.py Lib/compiler/pyassem.py Lib/compiler/pycodegen.py Lib/compiler/transformer.py Lib/test/output/test_extcall Lib/test/test_ast.py Lib/test/test_code.py Lib/test/test_compiler.py Lib/test/test_frozen.py Lib/test/test_grammar.py Lib/test/test_new.py Modules/_ctypes/callbacks.c Modules/parsermodule.c Modules/pyexpat.c Objects/codeobject.c Objects/funcobject.c Parser/Python.asdl Python/Python-ast.c Python/ast.c Python/ceval.c Python/compile.c Python/graminit.c Python/import.c Python/marshal.c Python/symtable.c Tools/compiler/ast.txt Message-ID: <20061027233154.76C391E4004@bag.python.org> Author: guido.van.rossum Date: Sat Oct 28 01:31:49 2006 New Revision: 52491 Modified: python/branches/p3yk/Grammar/Grammar python/branches/p3yk/Include/Python-ast.h python/branches/p3yk/Include/code.h python/branches/p3yk/Include/eval.h python/branches/p3yk/Include/funcobject.h python/branches/p3yk/Lib/compiler/ast.py python/branches/p3yk/Lib/compiler/pyassem.py python/branches/p3yk/Lib/compiler/pycodegen.py python/branches/p3yk/Lib/compiler/transformer.py python/branches/p3yk/Lib/test/output/test_extcall python/branches/p3yk/Lib/test/test_ast.py python/branches/p3yk/Lib/test/test_code.py python/branches/p3yk/Lib/test/test_compiler.py python/branches/p3yk/Lib/test/test_frozen.py python/branches/p3yk/Lib/test/test_grammar.py python/branches/p3yk/Lib/test/test_new.py python/branches/p3yk/Modules/_ctypes/callbacks.c python/branches/p3yk/Modules/parsermodule.c python/branches/p3yk/Modules/pyexpat.c python/branches/p3yk/Objects/codeobject.c python/branches/p3yk/Objects/funcobject.c python/branches/p3yk/Parser/Python.asdl python/branches/p3yk/Python/Python-ast.c python/branches/p3yk/Python/ast.c python/branches/p3yk/Python/ceval.c python/branches/p3yk/Python/compile.c python/branches/p3yk/Python/graminit.c python/branches/p3yk/Python/import.c python/branches/p3yk/Python/marshal.c python/branches/p3yk/Python/symtable.c python/branches/p3yk/Tools/compiler/ast.txt Log: Jiwon Seo's PEP 3102 implementation. See SF#1549670. The compiler package has not yet been updated. Modified: python/branches/p3yk/Grammar/Grammar ============================================================================== --- python/branches/p3yk/Grammar/Grammar (original) +++ python/branches/p3yk/Grammar/Grammar Sat Oct 28 01:31:49 2006 @@ -24,7 +24,7 @@ funcdef: [decorators] 'def' NAME parameters ':' suite parameters: '(' [varargslist] ')' varargslist: ((fpdef ['=' test] ',')* - ('*' NAME [',' '**' NAME] | '**' NAME) | + ('*' [NAME] (',' NAME ['=' test])* [',' '**' NAME] | '**' NAME) | fpdef ['=' test] (',' fpdef ['=' test])* [',']) fpdef: NAME | '(' fplist ')' fplist: fpdef (',' fpdef)* [','] Modified: python/branches/p3yk/Include/Python-ast.h ============================================================================== --- python/branches/p3yk/Include/Python-ast.h (original) +++ python/branches/p3yk/Include/Python-ast.h Sat Oct 28 01:31:49 2006 @@ -328,8 +328,10 @@ struct _arguments { asdl_seq *args; identifier vararg; + asdl_seq *kwonlyargs; identifier kwarg; asdl_seq *defaults; + asdl_seq *kw_defaults; }; struct _keyword { @@ -427,8 +429,9 @@ PyArena *arena); excepthandler_ty excepthandler(expr_ty type, expr_ty name, asdl_seq * body, int lineno, int col_offset, PyArena *arena); -arguments_ty arguments(asdl_seq * args, identifier vararg, identifier kwarg, - asdl_seq * defaults, PyArena *arena); +arguments_ty arguments(asdl_seq * args, identifier vararg, asdl_seq * + kwonlyargs, identifier kwarg, asdl_seq * defaults, + asdl_seq * kw_defaults, PyArena *arena); keyword_ty keyword(identifier arg, expr_ty value, PyArena *arena); alias_ty alias(identifier name, identifier asname, PyArena *arena); Modified: python/branches/p3yk/Include/code.h ============================================================================== --- python/branches/p3yk/Include/code.h (original) +++ python/branches/p3yk/Include/code.h Sat Oct 28 01:31:49 2006 @@ -10,6 +10,7 @@ typedef struct { PyObject_HEAD int co_argcount; /* #arguments, except *args */ + int co_kwonlyargcount; /* #keyword only arguments */ int co_nlocals; /* #local variables */ int co_stacksize; /* #entries needed for evaluation stack */ int co_flags; /* CO_..., see below */ @@ -63,8 +64,9 @@ /* Public interface */ PyAPI_FUNC(PyCodeObject *) PyCode_New( - int, int, int, int, PyObject *, PyObject *, PyObject *, PyObject *, - PyObject *, PyObject *, PyObject *, PyObject *, int, PyObject *); + int, int, int, int, int, PyObject *, PyObject *, + PyObject *, PyObject *, PyObject *, PyObject *, + PyObject *, PyObject *, int, PyObject *); /* same as struct above */ PyAPI_FUNC(int) PyCode_Addr2Line(PyCodeObject *, int); Modified: python/branches/p3yk/Include/eval.h ============================================================================== --- python/branches/p3yk/Include/eval.h (original) +++ python/branches/p3yk/Include/eval.h Sat Oct 28 01:31:49 2006 @@ -15,7 +15,7 @@ PyObject **args, int argc, PyObject **kwds, int kwdc, PyObject **defs, int defc, - PyObject *closure); + PyObject *kwdefs, PyObject *closure); PyAPI_FUNC(PyObject *) _PyEval_CallTracing(PyObject *func, PyObject *args); Modified: python/branches/p3yk/Include/funcobject.h ============================================================================== --- python/branches/p3yk/Include/funcobject.h (original) +++ python/branches/p3yk/Include/funcobject.h Sat Oct 28 01:31:49 2006 @@ -23,6 +23,7 @@ PyObject *func_code; /* A code object */ PyObject *func_globals; /* A dictionary (other mappings won't do) */ PyObject *func_defaults; /* NULL or a tuple */ + PyObject *func_kwdefaults; /* NULL or a dict */ PyObject *func_closure; /* NULL or a tuple of cell objects */ PyObject *func_doc; /* The __doc__ attribute, can be anything */ PyObject *func_name; /* The __name__ attribute, a string object */ @@ -47,6 +48,8 @@ PyAPI_FUNC(PyObject *) PyFunction_GetModule(PyObject *); PyAPI_FUNC(PyObject *) PyFunction_GetDefaults(PyObject *); PyAPI_FUNC(int) PyFunction_SetDefaults(PyObject *, PyObject *); +PyAPI_FUNC(PyObject *) PyFunction_GetKwDefaults(PyObject *); +PyAPI_FUNC(int) PyFunction_SetKwDefaults(PyObject *, PyObject *); PyAPI_FUNC(PyObject *) PyFunction_GetClosure(PyObject *); PyAPI_FUNC(int) PyFunction_SetClosure(PyObject *, PyObject *); @@ -60,6 +63,8 @@ (((PyFunctionObject *)func) -> func_module) #define PyFunction_GET_DEFAULTS(func) \ (((PyFunctionObject *)func) -> func_defaults) +#define PyFunction_GET_KW_DEFAULTS(func) \ + (((PyFunctionObject *)func) -> func_kwdefaults) #define PyFunction_GET_CLOSURE(func) \ (((PyFunctionObject *)func) -> func_closure) Modified: python/branches/p3yk/Lib/compiler/ast.py ============================================================================== --- python/branches/p3yk/Lib/compiler/ast.py (original) +++ python/branches/p3yk/Lib/compiler/ast.py Sat Oct 28 01:31:49 2006 @@ -487,11 +487,12 @@ return "From(%s, %s, %s)" % (repr(self.modname), repr(self.names), repr(self.level)) class Function(Node): - def __init__(self, decorators, name, argnames, defaults, flags, doc, code, lineno=None): + def __init__(self, decorators, name, argnames, defaults, kwonlyargs, flags, doc, code, lineno=None): self.decorators = decorators self.name = name self.argnames = argnames self.defaults = defaults + self.kwonlyargs = kwonlyargs self.flags = flags self.doc = doc self.code = code @@ -509,6 +510,7 @@ children.append(self.name) children.append(self.argnames) children.extend(flatten(self.defaults)) + children.append(self.kwonlyargs) children.append(self.flags) children.append(self.doc) children.append(self.code) @@ -523,7 +525,7 @@ return tuple(nodelist) def __repr__(self): - return "Function(%s, %s, %s, %s, %s, %s, %s)" % (repr(self.decorators), repr(self.name), repr(self.argnames), repr(self.defaults), repr(self.flags), repr(self.doc), repr(self.code)) + return "Function(%s, %s, %s, %s, %s, %s, %s, %s)" % (repr(self.decorators), repr(self.name), repr(self.argnames), repr(self.defaults), repr(self.kwonlyargs), repr(self.flags), repr(self.doc), repr(self.code)) class GenExpr(Node): def __init__(self, code, lineno=None): @@ -531,6 +533,7 @@ self.lineno = lineno self.argnames = ['.0'] self.varargs = self.kwargs = None + self.kwonlyargs = () def getChildren(self): @@ -713,9 +716,10 @@ return "Keyword(%s, %s)" % (repr(self.name), repr(self.expr)) class Lambda(Node): - def __init__(self, argnames, defaults, flags, code, lineno=None): + def __init__(self, argnames, defaults, kwonlyargs, flags, code, lineno=None): self.argnames = argnames self.defaults = defaults + self.kwonlyargs = kwonlyargs self.flags = flags self.code = code self.lineno = lineno @@ -730,6 +734,7 @@ children = [] children.append(self.argnames) children.extend(flatten(self.defaults)) + children.append(self.kwonlyargs) children.append(self.flags) children.append(self.code) return tuple(children) @@ -741,7 +746,7 @@ return tuple(nodelist) def __repr__(self): - return "Lambda(%s, %s, %s, %s)" % (repr(self.argnames), repr(self.defaults), repr(self.flags), repr(self.code)) + return "Lambda(%s, %s, %s, %s, %s)" % (repr(self.argnames), repr(self.defaults), repr(self.kwonlyargs), repr(self.flags), repr(self.code)) class LeftShift(Node): def __init__(self, (left, right), lineno=None): Modified: python/branches/p3yk/Lib/compiler/pyassem.py ============================================================================== --- python/branches/p3yk/Lib/compiler/pyassem.py (original) +++ python/branches/p3yk/Lib/compiler/pyassem.py Sat Oct 28 01:31:49 2006 @@ -313,13 +313,15 @@ class PyFlowGraph(FlowGraph): super_init = FlowGraph.__init__ - def __init__(self, name, filename, args=(), optimized=0, klass=None): + def __init__(self, name, filename, + args=(), kwonlyargs={}, optimized=0, klass=None): self.super_init() self.name = name self.filename = filename self.docstring = None self.args = args # XXX self.argcount = getArgCount(args) + self.kwonlyargs = kwonlyargs self.klass = klass if optimized: self.flags = CO_OPTIMIZED | CO_NEWLOCALS @@ -595,7 +597,9 @@ argcount = self.argcount if self.flags & CO_VARKEYWORDS: argcount = argcount - 1 - return new.code(argcount, nlocals, self.stacksize, self.flags, + kwonlyargcount = len(self.kwonlyargs) + return new.code(argcount, kwonlyargcount, + nlocals, self.stacksize, self.flags, self.lnotab.getCode(), self.getConsts(), tuple(self.names), tuple(self.varnames), self.filename, self.name, self.lnotab.firstline, @@ -804,7 +808,8 @@ def CALL_FUNCTION_VAR_KW(self, argc): return self.CALL_FUNCTION(argc)-2 def MAKE_FUNCTION(self, argc): - return -argc + hi, lo = divmod(argc, 256) + return -(lo + hi * 2) def MAKE_CLOSURE(self, argc): # XXX need to account for free variables too! return -argc Modified: python/branches/p3yk/Lib/compiler/pycodegen.py ============================================================================== --- python/branches/p3yk/Lib/compiler/pycodegen.py (original) +++ python/branches/p3yk/Lib/compiler/pycodegen.py Sat Oct 28 01:31:49 2006 @@ -378,6 +378,12 @@ walk(node.code, gen) gen.finish() self.set_lineno(node) + for keyword in node.kwonlyargs: + default = keyword.expr + if isinstance(default, ast.EmptyNode): + continue + self.emit('LOAD_CONST', keyword.name) + self.visit(default) for default in node.defaults: self.visit(default) self._makeClosure(gen, len(node.defaults)) @@ -1320,7 +1326,9 @@ name = func.name args, hasTupleArg = generateArgList(func.argnames) + kwonlyargs = generateKwonlyArgList(func.kwonlyargs) self.graph = pyassem.PyFlowGraph(name, func.filename, args, + kwonlyargs=kwonlyargs, optimized=1) self.isLambda = isLambda self.super_init() @@ -1456,6 +1464,13 @@ raise ValueError, "unexpect argument type:", elt return args + extra, count +def generateKwonlyArgList(keywordOnlyArgs): + kwonlyargs = {} + for elt in keywordOnlyArgs: + assert isinstance(elt, ast.Keyword) + kwonlyargs[elt.name] = elt.expr + return kwonlyargs + def findOp(node): """Find the op (DELETE, LOAD, STORE) in an AssTuple tree""" v = OpFinder() Modified: python/branches/p3yk/Lib/compiler/transformer.py ============================================================================== --- python/branches/p3yk/Lib/compiler/transformer.py (original) +++ python/branches/p3yk/Lib/compiler/transformer.py Sat Oct 28 01:31:49 2006 @@ -250,9 +250,9 @@ args = nodelist[-3][2] if args[0] == symbol.varargslist: - names, defaults, flags = self.com_arglist(args[1:]) + names, defaults, kwonlyargs, flags = self.com_arglist(args[1:]) else: - names = defaults = () + names = defaults = kwonlyargs = () flags = 0 doc = self.get_docstring(nodelist[-1]) @@ -263,21 +263,23 @@ assert isinstance(code, Stmt) assert isinstance(code.nodes[0], Discard) del code.nodes[0] - return Function(decorators, name, names, defaults, flags, doc, code, - lineno=lineno) + return Function(decorators, name, names, defaults, + kwonlyargs, flags, doc, code, lineno=lineno) def lambdef(self, nodelist): # lambdef: 'lambda' [varargslist] ':' test if nodelist[2][0] == symbol.varargslist: - names, defaults, flags = self.com_arglist(nodelist[2][1:]) + names, defaults, kwonlyargs, flags = \ + self.com_arglist(nodelist[2][1:]) else: - names = defaults = () + names = defaults = kwonlyargs = () flags = 0 # code for lambda code = self.com_node(nodelist[-1]) - return Lambda(names, defaults, flags, code, lineno=nodelist[1][2]) + return Lambda(names, defaults, kwonlyargs, + flags, code, lineno=nodelist[1][2]) old_lambdef = lambdef def classdef(self, nodelist): @@ -783,13 +785,37 @@ # ('const', xxxx)) Nodes) return Discard(Const(None)) + def keywordonlyargs(self, nodelist): + # (',' NAME ['=' test])* + # ^^^ + # ------+ + kwonlyargs = [] + i = 0 + while i < len(nodelist): + default = EmptyNode() + node = nodelist[i] + #assert node[0] == token.COMMA + #node = nodelist[i+1] + if i+1 < len(nodelist) and nodelist[i+1][0] == token.EQUAL: + assert i+2 < len(nodelist) + default = self.com_node(nodelist[i+2]) + i += 2 + if node[0] == token.DOUBLESTAR: + return kwonlyargs, i + elif node[0] == token.NAME: + kwonlyargs.append(Keyword(node[1], default, lineno=node[2])) + i += 2 + return kwonlyargs, i + def com_arglist(self, nodelist): # varargslist: - # (fpdef ['=' test] ',')* ('*' NAME [',' '**' NAME] | '**' NAME) - # | fpdef ['=' test] (',' fpdef ['=' test])* [','] + # (fpdef ['=' test] ',')* + # ('*' [NAME] (',' NAME '=' test)* [',' '**' NAME] | '**' NAME) + # | fpdef ['=' test] (',' fpdef ['=' test])* [','] # fpdef: NAME | '(' fplist ')' # fplist: fpdef (',' fpdef)* [','] names = [] + kwonlyargs = [] defaults = [] flags = 0 @@ -799,10 +825,22 @@ if node[0] == token.STAR or node[0] == token.DOUBLESTAR: if node[0] == token.STAR: node = nodelist[i+1] - if node[0] == token.NAME: + if node[0] == token.NAME: # vararg names.append(node[1]) flags = flags | CO_VARARGS i = i + 3 + else: # no vararg + assert node[0] == token.COMMA + i += 1 + #elif node[0] == token.COMMA: + # i += 1 + # kwonlyargs, skip = self.keywordonlyargs(nodelist[i:]) + # i += skip + if nodelist[i][0] == token.NAME: + kwonlyargs, skip = self.keywordonlyargs(nodelist[i:]) + i += skip + + print "kwonlyargs:", kwonlyargs if i < len(nodelist): # should be DOUBLESTAR @@ -831,7 +869,8 @@ # skip the comma i = i + 1 - return names, defaults, flags + print "names:", names, "defaults:", defaults, "kwonlyargs:", kwonlyargs, "flags:", flags + return names, defaults, kwonlyargs, flags def com_fpdef(self, node): # fpdef: NAME | '(' fplist ')' Modified: python/branches/p3yk/Lib/test/output/test_extcall ============================================================================== --- python/branches/p3yk/Lib/test/output/test_extcall (original) +++ python/branches/p3yk/Lib/test/output/test_extcall Sat Oct 28 01:31:49 2006 @@ -9,9 +9,9 @@ (1, 2, 3) {'a': 4, 'b': 5} (1, 2, 3, 4, 5) {'a': 6, 'b': 7} (1, 2, 3, 6, 7) {'a': 8, 'b': 9, 'x': 4, 'y': 5} -TypeError: g() takes at least 1 argument (0 given) -TypeError: g() takes at least 1 argument (0 given) -TypeError: g() takes at least 1 argument (0 given) +TypeError: g() takes at least 1 positional argument (0 given) +TypeError: g() takes at least 1 positional argument (0 given) +TypeError: g() takes at least 1 positional argument (0 given) 1 () {} 1 (2,) {} 1 (2, 3) {} @@ -35,24 +35,24 @@ 3 512 True 3 3 -za () {} -> za() takes exactly 1 argument (0 given) +za () {} -> za() takes exactly 1 positional argument (0 given) za () {'a': 'aa'} -> ok za aa B D E V a za () {'d': 'dd'} -> za() got an unexpected keyword argument 'd' za () {'a': 'aa', 'd': 'dd'} -> za() got an unexpected keyword argument 'd' za () {'a': 'aa', 'b': 'bb', 'd': 'dd', 'e': 'ee'} -> za() got an unexpected keyword argument 'b' -za (1, 2) {} -> za() takes exactly 1 argument (2 given) -za (1, 2) {'a': 'aa'} -> za() takes exactly 1 non-keyword argument (2 given) -za (1, 2) {'d': 'dd'} -> za() takes exactly 1 non-keyword argument (2 given) -za (1, 2) {'a': 'aa', 'd': 'dd'} -> za() takes exactly 1 non-keyword argument (2 given) -za (1, 2) {'a': 'aa', 'b': 'bb', 'd': 'dd', 'e': 'ee'} -> za() takes exactly 1 non-keyword argument (2 given) -za (1, 2, 3, 4, 5) {} -> za() takes exactly 1 argument (5 given) -za (1, 2, 3, 4, 5) {'a': 'aa'} -> za() takes exactly 1 non-keyword argument (5 given) -za (1, 2, 3, 4, 5) {'d': 'dd'} -> za() takes exactly 1 non-keyword argument (5 given) -za (1, 2, 3, 4, 5) {'a': 'aa', 'd': 'dd'} -> za() takes exactly 1 non-keyword argument (5 given) -za (1, 2, 3, 4, 5) {'a': 'aa', 'b': 'bb', 'd': 'dd', 'e': 'ee'} -> za() takes exactly 1 non-keyword argument (5 given) -zade () {} -> zade() takes at least 1 argument (0 given) +za (1, 2) {} -> za() takes exactly 1 positional argument (2 given) +za (1, 2) {'a': 'aa'} -> za() takes exactly 1 non-keyword positional argument (2 given) +za (1, 2) {'d': 'dd'} -> za() takes exactly 1 non-keyword positional argument (2 given) +za (1, 2) {'a': 'aa', 'd': 'dd'} -> za() takes exactly 1 non-keyword positional argument (2 given) +za (1, 2) {'a': 'aa', 'b': 'bb', 'd': 'dd', 'e': 'ee'} -> za() takes exactly 1 non-keyword positional argument (2 given) +za (1, 2, 3, 4, 5) {} -> za() takes exactly 1 positional argument (5 given) +za (1, 2, 3, 4, 5) {'a': 'aa'} -> za() takes exactly 1 non-keyword positional argument (5 given) +za (1, 2, 3, 4, 5) {'d': 'dd'} -> za() takes exactly 1 non-keyword positional argument (5 given) +za (1, 2, 3, 4, 5) {'a': 'aa', 'd': 'dd'} -> za() takes exactly 1 non-keyword positional argument (5 given) +za (1, 2, 3, 4, 5) {'a': 'aa', 'b': 'bb', 'd': 'dd', 'e': 'ee'} -> za() takes exactly 1 non-keyword positional argument (5 given) +zade () {} -> zade() takes at least 1 positional argument (0 given) zade () {'a': 'aa'} -> ok zade aa B d e V a -zade () {'d': 'dd'} -> zade() takes at least 1 non-keyword argument (0 given) +zade () {'d': 'dd'} -> zade() takes at least 1 non-keyword positional argument (0 given) zade () {'a': 'aa', 'd': 'dd'} -> ok zade aa B dd e V d zade () {'a': 'aa', 'b': 'bb', 'd': 'dd', 'e': 'ee'} -> zade() got an unexpected keyword argument 'b' zade (1, 2) {} -> ok zade 1 B 2 e V e @@ -60,30 +60,30 @@ zade (1, 2) {'d': 'dd'} -> zade() got multiple values for keyword argument 'd' zade (1, 2) {'a': 'aa', 'd': 'dd'} -> zade() got multiple values for keyword argument 'a' zade (1, 2) {'a': 'aa', 'b': 'bb', 'd': 'dd', 'e': 'ee'} -> zade() got multiple values for keyword argument 'a' -zade (1, 2, 3, 4, 5) {} -> zade() takes at most 3 arguments (5 given) -zade (1, 2, 3, 4, 5) {'a': 'aa'} -> zade() takes at most 3 non-keyword arguments (5 given) -zade (1, 2, 3, 4, 5) {'d': 'dd'} -> zade() takes at most 3 non-keyword arguments (5 given) -zade (1, 2, 3, 4, 5) {'a': 'aa', 'd': 'dd'} -> zade() takes at most 3 non-keyword arguments (5 given) -zade (1, 2, 3, 4, 5) {'a': 'aa', 'b': 'bb', 'd': 'dd', 'e': 'ee'} -> zade() takes at most 3 non-keyword arguments (5 given) -zabk () {} -> zabk() takes exactly 2 arguments (0 given) -zabk () {'a': 'aa'} -> zabk() takes exactly 2 non-keyword arguments (1 given) -zabk () {'d': 'dd'} -> zabk() takes exactly 2 non-keyword arguments (0 given) -zabk () {'a': 'aa', 'd': 'dd'} -> zabk() takes exactly 2 non-keyword arguments (1 given) +zade (1, 2, 3, 4, 5) {} -> zade() takes at most 3 positional arguments (5 given) +zade (1, 2, 3, 4, 5) {'a': 'aa'} -> zade() takes at most 3 non-keyword positional arguments (5 given) +zade (1, 2, 3, 4, 5) {'d': 'dd'} -> zade() takes at most 3 non-keyword positional arguments (5 given) +zade (1, 2, 3, 4, 5) {'a': 'aa', 'd': 'dd'} -> zade() takes at most 3 non-keyword positional arguments (5 given) +zade (1, 2, 3, 4, 5) {'a': 'aa', 'b': 'bb', 'd': 'dd', 'e': 'ee'} -> zade() takes at most 3 non-keyword positional arguments (5 given) +zabk () {} -> zabk() takes exactly 2 positional arguments (0 given) +zabk () {'a': 'aa'} -> zabk() takes exactly 2 non-keyword positional arguments (1 given) +zabk () {'d': 'dd'} -> zabk() takes exactly 2 non-keyword positional arguments (0 given) +zabk () {'a': 'aa', 'd': 'dd'} -> zabk() takes exactly 2 non-keyword positional arguments (1 given) zabk () {'a': 'aa', 'b': 'bb', 'd': 'dd', 'e': 'ee'} -> ok zabk aa bb D E V {'d': 'dd', 'e': 'ee'} zabk (1, 2) {} -> ok zabk 1 2 D E V {} zabk (1, 2) {'a': 'aa'} -> zabk() got multiple values for keyword argument 'a' zabk (1, 2) {'d': 'dd'} -> ok zabk 1 2 D E V {'d': 'dd'} zabk (1, 2) {'a': 'aa', 'd': 'dd'} -> zabk() got multiple values for keyword argument 'a' zabk (1, 2) {'a': 'aa', 'b': 'bb', 'd': 'dd', 'e': 'ee'} -> zabk() got multiple values for keyword argument 'a' -zabk (1, 2, 3, 4, 5) {} -> zabk() takes exactly 2 arguments (5 given) -zabk (1, 2, 3, 4, 5) {'a': 'aa'} -> zabk() takes exactly 2 non-keyword arguments (5 given) -zabk (1, 2, 3, 4, 5) {'d': 'dd'} -> zabk() takes exactly 2 non-keyword arguments (5 given) -zabk (1, 2, 3, 4, 5) {'a': 'aa', 'd': 'dd'} -> zabk() takes exactly 2 non-keyword arguments (5 given) -zabk (1, 2, 3, 4, 5) {'a': 'aa', 'b': 'bb', 'd': 'dd', 'e': 'ee'} -> zabk() takes exactly 2 non-keyword arguments (5 given) -zabdv () {} -> zabdv() takes at least 2 arguments (0 given) -zabdv () {'a': 'aa'} -> zabdv() takes at least 2 non-keyword arguments (1 given) -zabdv () {'d': 'dd'} -> zabdv() takes at least 2 non-keyword arguments (0 given) -zabdv () {'a': 'aa', 'd': 'dd'} -> zabdv() takes at least 2 non-keyword arguments (1 given) +zabk (1, 2, 3, 4, 5) {} -> zabk() takes exactly 2 positional arguments (5 given) +zabk (1, 2, 3, 4, 5) {'a': 'aa'} -> zabk() takes exactly 2 non-keyword positional arguments (5 given) +zabk (1, 2, 3, 4, 5) {'d': 'dd'} -> zabk() takes exactly 2 non-keyword positional arguments (5 given) +zabk (1, 2, 3, 4, 5) {'a': 'aa', 'd': 'dd'} -> zabk() takes exactly 2 non-keyword positional arguments (5 given) +zabk (1, 2, 3, 4, 5) {'a': 'aa', 'b': 'bb', 'd': 'dd', 'e': 'ee'} -> zabk() takes exactly 2 non-keyword positional arguments (5 given) +zabdv () {} -> zabdv() takes at least 2 positional arguments (0 given) +zabdv () {'a': 'aa'} -> zabdv() takes at least 2 non-keyword positional arguments (1 given) +zabdv () {'d': 'dd'} -> zabdv() takes at least 2 non-keyword positional arguments (0 given) +zabdv () {'a': 'aa', 'd': 'dd'} -> zabdv() takes at least 2 non-keyword positional arguments (1 given) zabdv () {'a': 'aa', 'b': 'bb', 'd': 'dd', 'e': 'ee'} -> zabdv() got an unexpected keyword argument 'e' zabdv (1, 2) {} -> ok zabdv 1 2 d E () e zabdv (1, 2) {'a': 'aa'} -> zabdv() got multiple values for keyword argument 'a' @@ -95,10 +95,10 @@ zabdv (1, 2, 3, 4, 5) {'d': 'dd'} -> zabdv() got multiple values for keyword argument 'd' zabdv (1, 2, 3, 4, 5) {'a': 'aa', 'd': 'dd'} -> zabdv() got multiple values for keyword argument 'a' zabdv (1, 2, 3, 4, 5) {'a': 'aa', 'b': 'bb', 'd': 'dd', 'e': 'ee'} -> zabdv() got multiple values for keyword argument 'a' -zabdevk () {} -> zabdevk() takes at least 2 arguments (0 given) -zabdevk () {'a': 'aa'} -> zabdevk() takes at least 2 non-keyword arguments (1 given) -zabdevk () {'d': 'dd'} -> zabdevk() takes at least 2 non-keyword arguments (0 given) -zabdevk () {'a': 'aa', 'd': 'dd'} -> zabdevk() takes at least 2 non-keyword arguments (1 given) +zabdevk () {} -> zabdevk() takes at least 2 positional arguments (0 given) +zabdevk () {'a': 'aa'} -> zabdevk() takes at least 2 non-keyword positional arguments (1 given) +zabdevk () {'d': 'dd'} -> zabdevk() takes at least 2 non-keyword positional arguments (0 given) +zabdevk () {'a': 'aa', 'd': 'dd'} -> zabdevk() takes at least 2 non-keyword positional arguments (1 given) zabdevk () {'a': 'aa', 'b': 'bb', 'd': 'dd', 'e': 'ee'} -> ok zabdevk aa bb dd ee () {} zabdevk (1, 2) {} -> ok zabdevk 1 2 d e () {} zabdevk (1, 2) {'a': 'aa'} -> zabdevk() got multiple values for keyword argument 'a' Modified: python/branches/p3yk/Lib/test/test_ast.py ============================================================================== --- python/branches/p3yk/Lib/test/test_ast.py (original) +++ python/branches/p3yk/Lib/test/test_ast.py Sat Oct 28 01:31:49 2006 @@ -151,9 +151,9 @@ #### EVERYTHING BELOW IS GENERATED ##### exec_results = [ -('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [], None, None, []), [('Pass', (1, 9))], [])]), +('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [], None, [], None, [], []), [('Pass', (1, 9))], [])]), ('Module', [('ClassDef', (1, 0), 'C', [], [('Pass', (1, 8))])]), -('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [], None, None, []), [('Return', (1, 8), ('Num', (1, 15), 1))], [])]), +('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [], None, [], None, [], []), [('Return', (1, 8), ('Num', (1, 15), 1))], [])]), ('Module', [('Delete', (1, 0), [('Name', (1, 4), 'v', ('Del',))])]), ('Module', [('Assign', (1, 0), [('Name', (1, 0), 'v', ('Store',))], ('Num', (1, 4), 1))]), ('Module', [('AugAssign', (1, 0), ('Name', (1, 0), 'v', ('Store',)), ('Add',), ('Num', (1, 5), 1))]), @@ -180,7 +180,7 @@ ('Expression', ('BoolOp', (1, 0), ('And',), [('Name', (1, 0), 'a', ('Load',)), ('Name', (1, 6), 'b', ('Load',))])), ('Expression', ('BinOp', (1, 0), ('Name', (1, 0), 'a', ('Load',)), ('Add',), ('Name', (1, 4), 'b', ('Load',)))), ('Expression', ('UnaryOp', (1, 0), ('Not',), ('Name', (1, 4), 'v', ('Load',)))), -('Expression', ('Lambda', (1, 0), ('arguments', [], None, None, []), ('Name', (1, 7), 'None', ('Load',)))), +('Expression', ('Lambda', (1, 0), ('arguments', [], None, [], None, [], []), ('Name', (1, 7), 'None', ('Load',)))), ('Expression', ('Dict', (1, 0), [('Num', (1, 2), 1)], [('Num', (1, 4), 2)])), ('Expression', ('ListComp', (1, 1), ('Name', (1, 1), 'a', ('Load',)), [('comprehension', ('Name', (1, 7), 'b', ('Store',)), ('Name', (1, 12), 'c', ('Load',)), [('Name', (1, 17), 'd', ('Load',))])])), ('Expression', ('GeneratorExp', (1, 1), ('Name', (1, 1), 'a', ('Load',)), [('comprehension', ('Name', (1, 7), 'b', ('Store',)), ('Name', (1, 12), 'c', ('Load',)), [('Name', (1, 17), 'd', ('Load',))])])), Modified: python/branches/p3yk/Lib/test/test_code.py ============================================================================== --- python/branches/p3yk/Lib/test/test_code.py (original) +++ python/branches/p3yk/Lib/test/test_code.py Sat Oct 28 01:31:49 2006 @@ -9,6 +9,7 @@ >>> dump(f.func_code) name: f argcount: 1 +kwonlyargcount: 0 names: () varnames: ('x', 'g') cellvars: ('x',) @@ -20,6 +21,7 @@ >>> dump(f(4).func_code) name: g argcount: 1 +kwonlyargcount: 0 names: () varnames: ('y',) cellvars: () @@ -34,9 +36,11 @@ ... c = a * b ... return c ... + >>> dump(h.func_code) name: h argcount: 2 +kwonlyargcount: 0 names: () varnames: ('x', 'y', 'a', 'b', 'c') cellvars: () @@ -53,6 +57,7 @@ >>> dump(attrs.func_code) name: attrs argcount: 1 +kwonlyargcount: 0 names: ('attr1', 'attr2', 'attr3') varnames: ('obj',) cellvars: () @@ -70,6 +75,7 @@ >>> dump(optimize_away.func_code) name: optimize_away argcount: 0 +kwonlyargcount: 0 names: () varnames: () cellvars: () @@ -78,6 +84,22 @@ flags: 67 consts: ("'doc string'", 'None') +>>> def keywordonly_args(a,b,*,k1): +... return a,b,k1 +... + +>>> dump(keywordonly_args.func_code) +name: keywordonly_args +argcount: 2 +kwonlyargcount: 1 +names: () +varnames: ('a', 'b', 'k1') +cellvars: () +freevars: () +nlocals: 3 +flags: 67 +consts: ('None',) + """ def consts(t): @@ -91,8 +113,8 @@ def dump(co): """Print out a text representation of a code object.""" - for attr in ["name", "argcount", "names", "varnames", "cellvars", - "freevars", "nlocals", "flags"]: + for attr in ["name", "argcount", "kwonlyargcount", "names", "varnames", + "cellvars", "freevars", "nlocals", "flags"]: print "%s: %s" % (attr, getattr(co, "co_" + attr)) print "consts:", tuple(consts(co.co_consts)) Modified: python/branches/p3yk/Lib/test/test_compiler.py ============================================================================== --- python/branches/p3yk/Lib/test/test_compiler.py (original) +++ python/branches/p3yk/Lib/test/test_compiler.py Sat Oct 28 01:31:49 2006 @@ -19,36 +19,51 @@ libdir = os.path.dirname(unittest.__file__) testdir = os.path.dirname(test.test_support.__file__) - for dir in [libdir, testdir]: - for basename in os.listdir(dir): - # Print still working message since this test can be really slow - if next_time <= time.time(): - next_time = time.time() + _PRINT_WORKING_MSG_INTERVAL - print >>sys.__stdout__, \ - ' testCompileLibrary still working, be patient...' - sys.__stdout__.flush() - - if not basename.endswith(".py"): - continue - if not TEST_ALL and random() < 0.98: - continue - path = os.path.join(dir, basename) - if test.test_support.verbose: - print "compiling", path - f = open(path, "U") - buf = f.read() - f.close() - if "badsyntax" in basename or "bad_coding" in basename: - self.assertRaises(SyntaxError, compiler.compile, - buf, basename, "exec") - else: - try: - compiler.compile(buf, basename, "exec") - except Exception, e: - args = list(e.args) - args[0] += "[in file %s]" % basename - e.args = tuple(args) - raise +## for dir in [libdir, testdir]: +## for basename in os.listdir(dir): +## # Print still working message since this test can be really slow +## if next_time <= time.time(): +## next_time = time.time() + _PRINT_WORKING_MSG_INTERVAL +## print >>sys.__stdout__, \ +## ' testCompileLibrary still working, be patient...' +## sys.__stdout__.flush() +## +## if not basename.endswith(".py"): +## continue +## if not TEST_ALL and random() < 0.98: +## continue +## path = os.path.join(dir, basename) +## if test.test_support.verbose: +## print "compiling", path +## f = open(path, "U") +## buf = f.read() +## f.close() +## if "badsyntax" in basename or "bad_coding" in basename: +## self.assertRaises(SyntaxError, compiler.compile, +## buf, basename, "exec") +## else: +## try: +## compiler.compile(buf, basename, "exec") +## except Exception, e: +## args = list(e.args) +## args[0] += "[in file %s]" % basename +## e.args = tuple(args) +## raise + + path = "/home/jiwon/p3yk/Lib/test/test_keywordonlyarg.py" + if test.test_support.verbose: + print "compiling", path + f = open(path, "U") + buf = f.read() + f.close() + #try: + compiler.compile(buf, "test_keywordonlyarg.py", "exec") + #except Exception, e: + # args = list(e.args) + # args[0] += "[in file %s]" % path + # e.args = tuple(args) + # raise + def testNewClassSyntax(self): compiler.compile("class foo():pass\n\n","","exec") Modified: python/branches/p3yk/Lib/test/test_frozen.py ============================================================================== --- python/branches/p3yk/Lib/test/test_frozen.py (original) +++ python/branches/p3yk/Lib/test/test_frozen.py Sat Oct 28 01:31:49 2006 @@ -1,4 +1,11 @@ # Test the frozen module defined in frozen.c. +# Currently test_frozen fails: +# Implementing pep3102(keyword only argument) needs changes in +# code object, which needs modification to marshal. +# However, to regenerate hard-coded marshal data in frozen.c, +# we need to run Tools/freeze/freeze.py, which currently doesn't work +# because Lib/modulefinder.py cannot handle relative module import +# This test will keep failing until Lib/modulefinder.py is fixed from test.test_support import TestFailed import sys, os Modified: python/branches/p3yk/Lib/test/test_grammar.py ============================================================================== --- python/branches/p3yk/Lib/test/test_grammar.py (original) +++ python/branches/p3yk/Lib/test/test_grammar.py Sat Oct 28 01:31:49 2006 @@ -148,7 +148,8 @@ print 'funcdef' ### 'def' NAME parameters ':' suite ### parameters: '(' [varargslist] ')' -### varargslist: (fpdef ['=' test] ',')* ('*' NAME [',' ('**'|'*' '*') NAME] +### varargslist: (fpdef ['=' test] ',')* +### ('*' (NAME|',' fpdef ['=' test]) [',' ('**'|'*' '*') NAME] ### | ('**'|'*' '*') NAME) ### | fpdef ['=' test] (',' fpdef ['=' test])* [','] ### fpdef: NAME | '(' fplist ')' @@ -265,6 +266,16 @@ d31v(1) def d32v((x,)): pass d32v((1,)) +#keyword only argument tests +def pos0key1(*, key): return key +pos0key1(key=100) +def pos2key2(p1, p2, *, k1, k2=100): return p1,p2,k1,k2 +pos2key2(1, 2, k1=100) +pos2key2(1, 2, k1=100, k2=200) +pos2key2(1, 2, k2=100, k1=200) +def pos2key2dict(p1, p2, *, k1=100, k2, **kwarg): return p1,p2,k1,k2,kwarg +pos2key2dict(1,2,k2=100,tokwarg1=100,tokwarg2=200) +pos2key2dict(1,2,tokwarg1=100,tokwarg2=200, k2=100) ### lambdef: 'lambda' [varargslist] ':' test print 'lambdef' @@ -279,6 +290,9 @@ verify(l5(1, 2) == 5) verify(l5(1, 2, 3) == 6) check_syntax("lambda x: x = 2") +l6 = lambda x, y, *, k=20: x+y+k +verify(l6(1,2) == 1+2+20) +verify(l6(1,2,k=10) == 1+2+10) ### stmt: simple_stmt | compound_stmt # Tested below Modified: python/branches/p3yk/Lib/test/test_new.py ============================================================================== --- python/branches/p3yk/Lib/test/test_new.py (original) +++ python/branches/p3yk/Lib/test/test_new.py Sat Oct 28 01:31:49 2006 @@ -103,6 +103,7 @@ c = f.func_code argcount = c.co_argcount + kwonlyargcount = c.co_kwonlyargcount nlocals = c.co_nlocals stacksize = c.co_stacksize flags = c.co_flags @@ -117,17 +118,20 @@ freevars = c.co_freevars cellvars = c.co_cellvars - d = new.code(argcount, nlocals, stacksize, flags, codestring, + d = new.code(argcount, kwonlyargcount, + nlocals, stacksize, flags, codestring, constants, names, varnames, filename, name, firstlineno, lnotab, freevars, cellvars) # test backwards-compatibility version with no freevars or cellvars - d = new.code(argcount, nlocals, stacksize, flags, codestring, + d = new.code(argcount, kwonlyargcount, + nlocals, stacksize, flags, codestring, constants, names, varnames, filename, name, firstlineno, lnotab) try: # this used to trigger a SystemError - d = new.code(-argcount, nlocals, stacksize, flags, codestring, + d = new.code(-argcount, kwonlyargcount, + nlocals, stacksize, flags, codestring, constants, names, varnames, filename, name, firstlineno, lnotab) except ValueError: @@ -136,7 +140,8 @@ raise TestFailed, "negative co_argcount didn't trigger an exception" try: # this used to trigger a SystemError - d = new.code(argcount, -nlocals, stacksize, flags, codestring, + d = new.code(argcount, kwonlyargcount, + -nlocals, stacksize, flags, codestring, constants, names, varnames, filename, name, firstlineno, lnotab) except ValueError: @@ -145,7 +150,8 @@ raise TestFailed, "negative co_nlocals didn't trigger an exception" try: # this used to trigger a Py_FatalError! - d = new.code(argcount, nlocals, stacksize, flags, codestring, + d = new.code(argcount, kwonlyargcount, + nlocals, stacksize, flags, codestring, constants, (5,), varnames, filename, name, firstlineno, lnotab) except TypeError: @@ -156,7 +162,8 @@ # new.code used to be a way to mutate a tuple... class S(str): pass t = (S("ab"),) - d = new.code(argcount, nlocals, stacksize, flags, codestring, + d = new.code(argcount, kwonlyargcount, + nlocals, stacksize, flags, codestring, constants, t, varnames, filename, name, firstlineno, lnotab) verify(type(t[0]) is S, "eek, tuple changed under us!") Modified: python/branches/p3yk/Modules/_ctypes/callbacks.c ============================================================================== --- python/branches/p3yk/Modules/_ctypes/callbacks.c (original) +++ python/branches/p3yk/Modules/_ctypes/callbacks.c Sat Oct 28 01:31:49 2006 @@ -51,6 +51,7 @@ if (!empty_string) goto bad; py_code = PyCode_New( 0, /*int argcount,*/ + 0, /*int kwonlyargcount,*/ 0, /*int nlocals,*/ 0, /*int stacksize,*/ 0, /*int flags,*/ Modified: python/branches/p3yk/Modules/parsermodule.c ============================================================================== --- python/branches/p3yk/Modules/parsermodule.c (original) +++ python/branches/p3yk/Modules/parsermodule.c Sat Oct 28 01:31:49 2006 @@ -1105,13 +1105,13 @@ } -/* '*' NAME [',' '**' NAME] | '**' NAME +/* '*' [NAME] (',' NAME ['=' test])* [',' '**' NAME] | '**' NAME */ static int validate_varargslist_trailer(node *tree, int start) { int nch = NCH(tree); - int res = 0; + int res = 0, i; int sym; if (nch <= start) { @@ -1121,15 +1121,40 @@ sym = TYPE(CHILD(tree, start)); if (sym == STAR) { /* - * ('*' NAME [',' '**' NAME] + * '*' [NAME] (',' NAME ['=' test])* [',' '**' NAME] | '**' NAME */ if (nch-start == 2) res = validate_name(CHILD(tree, start+1), NULL); - else if (nch-start == 5) + else if (nch-start == 5 && TYPE(CHILD(tree, start+2)) == COMMA) res = (validate_name(CHILD(tree, start+1), NULL) && validate_comma(CHILD(tree, start+2)) && validate_doublestar(CHILD(tree, start+3)) && validate_name(CHILD(tree, start+4), NULL)); + else { + /* skip over [NAME] (',' NAME ['=' test])* */ + i = start + 1; + if (TYPE(CHILD(tree, i)) == NAME) { /* skip over [NAME] */ + i += 1; + } + while (res && i+1 < nch) { /* validate (',' NAME ['=' test])* */ + res = validate_comma(CHILD(tree, i)); + if (TYPE(CHILD(tree, i+1)) == DOUBLESTAR) + break; + res = res && validate_name(CHILD(tree, i+1), NULL); + if (res && i+2 < nch && TYPE(CHILD(tree, i+2)) == EQUAL) { + res = res && (i+3 < nch) + && validate_test(CHILD(tree, i+3)); + i += 4; + } + else { + i += 2; + } + } + /* [',' '**' NAME] */ + if (res && i+1 < nch && TYPE(CHILD(tree, i+1)) == DOUBLESTAR) { + res = validate_name(CHILD(tree, i+2), NULL); + } + } } else if (sym == DOUBLESTAR) { /* @@ -1148,9 +1173,8 @@ * * varargslist: * (fpdef ['=' test] ',')* - * ('*' NAME [',' '**' NAME] - * | '**' NAME) - * | fpdef ['=' test] (',' fpdef ['=' test])* [','] + * ('*' [NAME] (',' NAME ['=' test])* [',' '**' NAME] | '**' NAME) + * | fpdef ['=' test] (',' fpdef ['=' test])* [','] * */ static int @@ -1169,7 +1193,7 @@ sym = TYPE(CHILD(tree, 0)); if (sym == STAR || sym == DOUBLESTAR) /* whole thing matches: - * '*' NAME [',' '**' NAME] | '**' NAME + * '*' [NAME] (',' NAME ['=' test])* [',' '**' NAME] | '**' NAME */ res = validate_varargslist_trailer(tree, 0); else if (sym == fpdef) { @@ -1201,7 +1225,7 @@ break; } } - /* ... '*' NAME [',' '**' NAME] | '**' NAME + /* .. ('*' [NAME] (',' NAME ['=' test])* [',' '**' NAME] | '**' NAME) * i --^^^ */ if (res) Modified: python/branches/p3yk/Modules/pyexpat.c ============================================================================== --- python/branches/p3yk/Modules/pyexpat.c (original) +++ python/branches/p3yk/Modules/pyexpat.c Sat Oct 28 01:31:49 2006 @@ -279,6 +279,7 @@ filename = PyString_FromString(__FILE__); handler_info[slot].tb_code = PyCode_New(0, /* argcount */ + 0, /* kwonlyargcount */ 0, /* nlocals */ 0, /* stacksize */ 0, /* flags */ Modified: python/branches/p3yk/Objects/codeobject.c ============================================================================== --- python/branches/p3yk/Objects/codeobject.c (original) +++ python/branches/p3yk/Objects/codeobject.c Sat Oct 28 01:31:49 2006 @@ -41,7 +41,8 @@ PyCodeObject * -PyCode_New(int argcount, int nlocals, int stacksize, int flags, +PyCode_New(int argcount, int kwonlyargcount, + int nlocals, int stacksize, int flags, PyObject *code, PyObject *consts, PyObject *names, PyObject *varnames, PyObject *freevars, PyObject *cellvars, PyObject *filename, PyObject *name, int firstlineno, @@ -80,6 +81,7 @@ co = PyObject_NEW(PyCodeObject, &PyCode_Type); if (co != NULL) { co->co_argcount = argcount; + co->co_kwonlyargcount = kwonlyargcount; co->co_nlocals = nlocals; co->co_stacksize = stacksize; co->co_flags = flags; @@ -112,6 +114,7 @@ static PyMemberDef code_memberlist[] = { {"co_argcount", T_INT, OFF(co_argcount), READONLY}, + {"co_kwonlyargcount", T_INT, OFF(co_kwonlyargcount), READONLY}, {"co_nlocals", T_INT, OFF(co_nlocals), READONLY}, {"co_stacksize",T_INT, OFF(co_stacksize), READONLY}, {"co_flags", T_INT, OFF(co_flags), READONLY}, @@ -182,6 +185,7 @@ code_new(PyTypeObject *type, PyObject *args, PyObject *kw) { int argcount; + int kwonlyargcount; int nlocals; int stacksize; int flags; @@ -197,8 +201,9 @@ int firstlineno; PyObject *lnotab; - if (!PyArg_ParseTuple(args, "iiiiSO!O!O!SSiS|O!O!:code", - &argcount, &nlocals, &stacksize, &flags, + if (!PyArg_ParseTuple(args, "iiiiiSO!O!O!SSiS|O!O!:code", + &argcount, &kwonlyargcount, + &nlocals, &stacksize, &flags, &code, &PyTuple_Type, &consts, &PyTuple_Type, &names, @@ -216,6 +221,12 @@ goto cleanup; } + if (kwonlyargcount < 0) { + PyErr_SetString( + PyExc_ValueError, + "code: kwonlyargcount must not be negative"); + goto cleanup; + } if (nlocals < 0) { PyErr_SetString( PyExc_ValueError, @@ -242,7 +253,8 @@ if (ourcellvars == NULL) goto cleanup; - co = (PyObject *)PyCode_New(argcount, nlocals, stacksize, flags, + co = (PyObject *)PyCode_New(argcount, kwonlyargcount, + nlocals, stacksize, flags, code, consts, ournames, ourvarnames, ourfreevars, ourcellvars, filename, name, firstlineno, lnotab); @@ -312,6 +324,8 @@ if (eq <= 0) goto unequal; eq = co->co_argcount == cp->co_argcount; if (!eq) goto unequal; + eq = co->co_kwonlyargcount == cp->co_kwonlyargcount; + if (!eq) goto unequal; eq = co->co_nlocals == cp->co_nlocals; if (!eq) goto unequal; eq = co->co_flags == cp->co_flags; @@ -369,7 +383,8 @@ h6 = PyObject_Hash(co->co_cellvars); if (h6 == -1) return -1; h = h0 ^ h1 ^ h2 ^ h3 ^ h4 ^ h5 ^ h6 ^ - co->co_argcount ^ co->co_nlocals ^ co->co_flags; + co->co_argcount ^ co->co_kwonlyargcount ^ + co->co_nlocals ^ co->co_flags; if (h == -1) h = -2; return h; } Modified: python/branches/p3yk/Objects/funcobject.c ============================================================================== --- python/branches/p3yk/Objects/funcobject.c (original) +++ python/branches/p3yk/Objects/funcobject.c Sat Oct 28 01:31:49 2006 @@ -24,6 +24,7 @@ op->func_name = ((PyCodeObject *)code)->co_name; Py_INCREF(op->func_name); op->func_defaults = NULL; /* No default arguments */ + op->func_kwdefaults = NULL; /* No keyword only defaults */ op->func_closure = NULL; consts = ((PyCodeObject *)code)->co_consts; if (PyTuple_Size(consts) >= 1) { @@ -122,6 +123,38 @@ } PyObject * +PyFunction_GetKwDefaults(PyObject *op) +{ + if (!PyFunction_Check(op)) { + PyErr_BadInternalCall(); + return NULL; + } + return ((PyFunctionObject *) op) -> func_kwdefaults; +} + +int +PyFunction_SetKwDefaults(PyObject *op, PyObject *defaults) +{ + if (!PyFunction_Check(op)) { + PyErr_BadInternalCall(); + return -1; + } + if (defaults == Py_None) + defaults = NULL; + else if (defaults && PyDict_Check(defaults)) { + Py_INCREF(defaults); + } + else { + PyErr_SetString(PyExc_SystemError, + "non-dict keyword only default args"); + return -1; + } + Py_XDECREF(((PyFunctionObject *)op) -> func_kwdefaults); + ((PyFunctionObject *) op) -> func_kwdefaults = defaults; + return 0; +} + +PyObject * PyFunction_GetClosure(PyObject *op) { if (!PyFunction_Check(op)) { @@ -325,10 +358,49 @@ return 0; } +static PyObject * +func_get_kwdefaults(PyFunctionObject *op) +{ + if (restricted()) + return NULL; + if (op->func_kwdefaults == NULL) { + Py_INCREF(Py_None); + return Py_None; + } + Py_INCREF(op->func_kwdefaults); + return op->func_kwdefaults; +} + +static int +func_set_kwdefaults(PyFunctionObject *op, PyObject *value) +{ + PyObject *tmp; + + if (restricted()) + return -1; + + if (value == Py_None) + value = NULL; + /* Legal to del f.func_defaults. + * Can only set func_kwdefaults to NULL or a dict. */ + if (value != NULL && !PyDict_Check(value)) { + PyErr_SetString(PyExc_TypeError, + "func_kwdefaults must be set to a dict object"); + return -1; + } + tmp = op->func_kwdefaults; + Py_XINCREF(value); + op->func_kwdefaults = value; + Py_XDECREF(tmp); + return 0; +} + static PyGetSetDef func_getsetlist[] = { {"func_code", (getter)func_get_code, (setter)func_set_code}, {"func_defaults", (getter)func_get_defaults, (setter)func_set_defaults}, + {"func_kwdefaults", (getter)func_get_kwdefaults, + (setter)func_set_kwdefaults}, {"func_dict", (getter)func_get_dict, (setter)func_set_dict}, {"__dict__", (getter)func_get_dict, (setter)func_set_dict}, {"func_name", (getter)func_get_name, (setter)func_set_name}, @@ -519,6 +591,7 @@ PyFunction_GET_GLOBALS(func), (PyObject *)NULL, &PyTuple_GET_ITEM(arg, 0), PyTuple_Size(arg), k, nk, d, nd, + PyFunction_GET_KW_DEFAULTS(func), PyFunction_GET_CLOSURE(func)); if (k != NULL) Modified: python/branches/p3yk/Parser/Python.asdl ============================================================================== --- python/branches/p3yk/Parser/Python.asdl (original) +++ python/branches/p3yk/Parser/Python.asdl Sat Oct 28 01:31:49 2006 @@ -100,8 +100,8 @@ excepthandler = (expr? type, expr? name, stmt* body, int lineno, int col_offset) - arguments = (expr* args, identifier? vararg, - identifier? kwarg, expr* defaults) + arguments = (expr* args, identifier? vararg, expr* kwonlyargs, + identifier? kwarg, expr* defaults, expr* kw_defaults) -- keyword arguments supplied to call keyword = (identifier arg, expr value) Modified: python/branches/p3yk/Python/Python-ast.c ============================================================================== --- python/branches/p3yk/Python/Python-ast.c (original) +++ python/branches/p3yk/Python/Python-ast.c Sat Oct 28 01:31:49 2006 @@ -333,8 +333,10 @@ static char *arguments_fields[]={ "args", "vararg", + "kwonlyargs", "kwarg", "defaults", + "kw_defaults", }; static PyTypeObject *keyword_type; static PyObject* ast2obj_keyword(void*); @@ -708,7 +710,7 @@ excepthandler_type = make_type("excepthandler", AST_type, excepthandler_fields, 5); if (!excepthandler_type) return 0; - arguments_type = make_type("arguments", AST_type, arguments_fields, 4); + arguments_type = make_type("arguments", AST_type, arguments_fields, 6); if (!arguments_type) return 0; keyword_type = make_type("keyword", AST_type, keyword_fields, 2); if (!keyword_type) return 0; @@ -1828,8 +1830,8 @@ } arguments_ty -arguments(asdl_seq * args, identifier vararg, identifier kwarg, asdl_seq * - defaults, PyArena *arena) +arguments(asdl_seq * args, identifier vararg, asdl_seq * kwonlyargs, identifier + kwarg, asdl_seq * defaults, asdl_seq * kw_defaults, PyArena *arena) { arguments_ty p; p = (arguments_ty)PyArena_Malloc(arena, sizeof(*p)); @@ -1839,8 +1841,10 @@ } p->args = args; p->vararg = vararg; + p->kwonlyargs = kwonlyargs; p->kwarg = kwarg; p->defaults = defaults; + p->kw_defaults = kw_defaults; return p; } @@ -2907,6 +2911,11 @@ if (PyObject_SetAttrString(result, "vararg", value) == -1) goto failed; Py_DECREF(value); + value = ast2obj_list(o->kwonlyargs, ast2obj_expr); + if (!value) goto failed; + if (PyObject_SetAttrString(result, "kwonlyargs", value) == -1) + goto failed; + Py_DECREF(value); value = ast2obj_identifier(o->kwarg); if (!value) goto failed; if (PyObject_SetAttrString(result, "kwarg", value) == -1) @@ -2917,6 +2926,11 @@ if (PyObject_SetAttrString(result, "defaults", value) == -1) goto failed; Py_DECREF(value); + value = ast2obj_list(o->kw_defaults, ast2obj_expr); + if (!value) goto failed; + if (PyObject_SetAttrString(result, "kw_defaults", value) == -1) + goto failed; + Py_DECREF(value); return result; failed: Py_XDECREF(value); @@ -2994,7 +3008,7 @@ if (PyDict_SetItemString(d, "AST", (PyObject*)AST_type) < 0) return; if (PyModule_AddIntConstant(m, "PyCF_ONLY_AST", PyCF_ONLY_AST) < 0) return; - if (PyModule_AddStringConstant(m, "__version__", "51631") < 0) + if (PyModule_AddStringConstant(m, "__version__", "51773") < 0) return; if (PyDict_SetItemString(d, "mod", (PyObject*)mod_type) < 0) return; if (PyDict_SetItemString(d, "Module", (PyObject*)Module_type) < 0) Modified: python/branches/p3yk/Python/ast.c ============================================================================== --- python/branches/p3yk/Python/ast.c (original) +++ python/branches/p3yk/Python/ast.c Sat Oct 28 01:31:49 2006 @@ -591,6 +591,63 @@ return result; } +/* returns -1 if failed to handle keyword only arguments + returns new position to keep processing if successful + (',' NAME ['=' test])* + ^^^ + start pointing here + */ +static int +handle_keywordonly_args(struct compiling *c, const node *n, int start, + asdl_seq *kwonlyargs, asdl_seq *kwdefaults) +{ + node *ch; + expr_ty name; + int i = start; + int j = 0; /* index for kwdefaults and kwonlyargs */ + assert(kwonlyargs != NULL); + assert(kwdefaults != NULL); + while (i < NCH(n)) { + ch = CHILD(n, i); + switch (TYPE(ch)) { + case NAME: + if (i + 1 < NCH(n) && TYPE(CHILD(n, i + 1)) == EQUAL) { + expr_ty expression = ast_for_expr(c, CHILD(n, i + 2)); + if (!expression) { + ast_error(ch, "assignment to None"); + goto error; + } + asdl_seq_SET(kwdefaults, j, expression); + i += 2; /* '=' and test */ + } + else { /* setting NULL if no default value exists */ + asdl_seq_SET(kwdefaults, j, NULL); + } + if (!strcmp(STR(ch), "None")) { + ast_error(ch, "assignment to None"); + goto error; + } + name = Name(NEW_IDENTIFIER(ch), + Param, LINENO(ch), ch->n_col_offset, + c->c_arena); + if (!name) { + ast_error(ch, "expecting name"); + goto error; + } + asdl_seq_SET(kwonlyargs, j++, name); + i += 2; /* the name and the comma */ + break; + case DOUBLESTAR: + return i; + default: + ast_error(ch, "unexpected node"); + goto error; + } + } + return i; + error: + return -1; +} /* Create AST for argument list. */ @@ -598,35 +655,71 @@ ast_for_arguments(struct compiling *c, const node *n) { /* parameters: '(' [varargslist] ')' - varargslist: (fpdef ['=' test] ',')* ('*' NAME [',' '**' NAME] - | '**' NAME) | fpdef ['=' test] (',' fpdef ['=' test])* [','] + varargslist: (fpdef ['=' test] ',')* + ('*' [NAME] (',' fpdef ['=' test])* [',' '**' NAME] | '**' NAME) + | fpdef ['=' test] (',' fpdef ['=' test])* [','] */ - int i, j, k, n_args = 0, n_defaults = 0, found_default = 0; - asdl_seq *args, *defaults; + int i, j, k, nposargs = 0, nkwonlyargs = 0; + int nposdefaults = 0, found_default = 0; + asdl_seq *posargs, *posdefaults, *kwonlyargs, *kwdefaults; identifier vararg = NULL, kwarg = NULL; node *ch; if (TYPE(n) == parameters) { - if (NCH(n) == 2) /* () as argument list */ - return arguments(NULL, NULL, NULL, NULL, c->c_arena); - n = CHILD(n, 1); + if (NCH(n) == 2) /* () as argument list */ + return arguments(NULL, NULL, NULL, NULL, NULL, NULL, c->c_arena); + n = CHILD(n, 1); } REQ(n, varargslist); - /* first count the number of normal args & defaults */ + /* first count the number of positional args & defaults */ for (i = 0; i < NCH(n); i++) { - ch = CHILD(n, i); - if (TYPE(ch) == fpdef) - n_args++; - if (TYPE(ch) == EQUAL) - n_defaults++; - } - args = (n_args ? asdl_seq_new(n_args, c->c_arena) : NULL); - if (!args && n_args) - return NULL; /* Don't need to goto error; no objects allocated */ - defaults = (n_defaults ? asdl_seq_new(n_defaults, c->c_arena) : NULL); - if (!defaults && n_defaults) - return NULL; /* Don't need to goto error; no objects allocated */ + ch = CHILD(n, i); + if (TYPE(ch) == STAR) { + if (TYPE(CHILD(n, i+1)) == NAME) { + /* skip NAME of vararg */ + /* so that following can count only keyword only args */ + i += 2; + } + else { + i++; + } + break; + } + if (TYPE(ch) == fpdef) nposargs++; + if (TYPE(ch) == EQUAL) nposdefaults++; + } + /* count the number of keyword only args & + defaults for keyword only args */ + for ( ; i < NCH(n); ++i) { + ch = CHILD(n, i); + if (TYPE(ch) == DOUBLESTAR) break; + if (TYPE(ch) == NAME) nkwonlyargs++; + } + + posargs = (nposargs ? asdl_seq_new(nposargs, c->c_arena) : NULL); + if (!posargs && nposargs) + return NULL; /* Don't need to goto error; no objects allocated */ + kwonlyargs = (nkwonlyargs ? + asdl_seq_new(nkwonlyargs, c->c_arena) : NULL); + if (!kwonlyargs && nkwonlyargs) + return NULL; /* Don't need to goto error; no objects allocated */ + posdefaults = (nposdefaults ? + asdl_seq_new(nposdefaults, c->c_arena) : NULL); + if (!posdefaults && nposdefaults) + return NULL; /* Don't need to goto error; no objects allocated */ + /* The length of kwonlyargs and kwdefaults are same + since we set NULL as default for keyword only argument w/o default + - we have sequence data structure, but no dictionary */ + kwdefaults = (nkwonlyargs ? + asdl_seq_new(nkwonlyargs, c->c_arena) : NULL); + if (!kwdefaults && nkwonlyargs) + return NULL; /* Don't need to goto error; no objects allocated */ + + if (nposargs + nkwonlyargs > 255) { + ast_error(n, "more than 255 arguments"); + return NULL; + } /* fpdef: NAME | '(' fplist ')' fplist: fpdef (',' fpdef)* [','] @@ -635,8 +728,8 @@ j = 0; /* index for defaults */ k = 0; /* index for args */ while (i < NCH(n)) { - ch = CHILD(n, i); - switch (TYPE(ch)) { + ch = CHILD(n, i); + switch (TYPE(ch)) { case fpdef: /* XXX Need to worry about checking if TYPE(CHILD(n, i+1)) is anything other than EQUAL or a comma? */ @@ -644,57 +737,80 @@ if (i + 1 < NCH(n) && TYPE(CHILD(n, i + 1)) == EQUAL) { expr_ty expression = ast_for_expr(c, CHILD(n, i + 2)); if (!expression) - goto error; - assert(defaults != NULL); - asdl_seq_SET(defaults, j++, expression); + goto error; + assert(posdefaults != NULL); + asdl_seq_SET(posdefaults, j++, expression); + i += 2; - found_default = 1; + found_default = 1; + } + else if (found_default) { + ast_error(n, + "non-default argument follows default argument"); + goto error; } - else if (found_default) { - ast_error(n, - "non-default argument follows default argument"); - goto error; - } if (NCH(ch) == 3) { - ch = CHILD(ch, 1); - /* def foo((x)): is not complex, special case. */ - if (NCH(ch) != 1) { - /* We have complex arguments, setup for unpacking. */ - asdl_seq_SET(args, k++, compiler_complex_args(c, ch)); - } else { - /* def foo((x)): setup for checking NAME below. */ - ch = CHILD(ch, 0); - } + ch = CHILD(ch, 1); + /* def foo((x)): is not complex, special case. */ + if (NCH(ch) != 1) { + /* We have complex arguments, setup for unpacking. */ + asdl_seq_SET(posargs, k++, + compiler_complex_args(c, ch)); + } else { + /* def foo((x)): setup for checking NAME below. */ + ch = CHILD(ch, 0); + } } if (TYPE(CHILD(ch, 0)) == NAME) { - expr_ty name; - if (!strcmp(STR(CHILD(ch, 0)), "None")) { - ast_error(CHILD(ch, 0), "assignment to None"); - goto error; - } + expr_ty name; + if (!strcmp(STR(CHILD(ch, 0)), "None")) { + ast_error(CHILD(ch, 0), "assignment to None"); + goto error; + } name = Name(NEW_IDENTIFIER(CHILD(ch, 0)), Param, LINENO(ch), ch->n_col_offset, c->c_arena); if (!name) goto error; - asdl_seq_SET(args, k++, name); - - } + asdl_seq_SET(posargs, k++, name); + + } i += 2; /* the name and the comma */ break; case STAR: - if (!strcmp(STR(CHILD(n, i+1)), "None")) { - ast_error(CHILD(n, i+1), "assignment to None"); - goto error; - } - vararg = NEW_IDENTIFIER(CHILD(n, i+1)); - i += 3; + if (i+1 >= NCH(n)) { + ast_error(CHILD(n, i), "no name for vararg"); + goto error; + } + if (!strcmp(STR(CHILD(n, i+1)), "None")) { + ast_error(CHILD(n, i+1), "assignment to None"); + goto error; + } + if (TYPE(CHILD(n, i+1)) == COMMA) { + int res = 0; + i += 2; /* now follows keyword only arguments */ + res = handle_keywordonly_args(c, n, i, + kwonlyargs, kwdefaults); + if (res == -1) goto error; + i = res; /* res has new position to process */ + } + else { + vararg = NEW_IDENTIFIER(CHILD(n, i+1)); + i += 3; + if (i < NCH(n) && TYPE(CHILD(n, i)) == NAME) { + int res = 0; + res = handle_keywordonly_args(c, n, i, + kwonlyargs, kwdefaults); + if (res == -1) goto error; + i = res; /* res has new position to process */ + } + } break; case DOUBLESTAR: - if (!strcmp(STR(CHILD(n, i+1)), "None")) { - ast_error(CHILD(n, i+1), "assignment to None"); - goto error; - } + if (!strcmp(STR(CHILD(n, i+1)), "None")) { + ast_error(CHILD(n, i+1), "assignment to None"); + goto error; + } kwarg = NEW_IDENTIFIER(CHILD(n, i+1)); i += 3; break; @@ -703,11 +819,10 @@ "unexpected node in varargslist: %d @ %d", TYPE(ch), i); goto error; - } + } } - - return arguments(args, vararg, kwarg, defaults, c->c_arena); - + return arguments(posargs, vararg, kwonlyargs, kwarg, + posdefaults, kwdefaults, c->c_arena); error: Py_XDECREF(vararg); Py_XDECREF(kwarg); @@ -851,7 +966,7 @@ expr_ty expression; if (NCH(n) == 3) { - args = arguments(NULL, NULL, NULL, NULL, c->c_arena); + args = arguments(NULL, NULL, NULL, NULL, NULL, NULL, c->c_arena); if (!args) return NULL; expression = ast_for_expr(c, CHILD(n, 2)); Modified: python/branches/p3yk/Python/ceval.c ============================================================================== --- python/branches/p3yk/Python/ceval.c (original) +++ python/branches/p3yk/Python/ceval.c Sat Oct 28 01:31:49 2006 @@ -494,7 +494,7 @@ (PyObject **)NULL, 0, (PyObject **)NULL, 0, (PyObject **)NULL, 0, - NULL); + NULL, NULL); } @@ -2290,26 +2290,46 @@ } case MAKE_FUNCTION: + { + int posdefaults = oparg & 0xff; + int kwdefaults = (oparg>>8) & 0xff; + v = POP(); /* code object */ x = PyFunction_New(v, f->f_globals); Py_DECREF(v); /* XXX Maybe this should be a separate opcode? */ - if (x != NULL && oparg > 0) { - v = PyTuple_New(oparg); + if (x != NULL && posdefaults > 0) { + v = PyTuple_New(posdefaults); if (v == NULL) { Py_DECREF(x); x = NULL; break; } - while (--oparg >= 0) { + while (--posdefaults >= 0) { w = POP(); - PyTuple_SET_ITEM(v, oparg, w); + PyTuple_SET_ITEM(v, posdefaults, w); } err = PyFunction_SetDefaults(x, v); Py_DECREF(v); } + if (x != NULL && kwdefaults > 0) { + v = PyDict_New(); + if (v == NULL) { + Py_DECREF(x); + x = NULL; + break; + } + while (--kwdefaults >= 0) { + w = POP(); /* default value */ + u = POP(); /* kw only arg name */ + PyDict_SetItem(v, u, w); + } + err = PyFunction_SetKwDefaults(x, v); + Py_DECREF(v); + } PUSH(x); break; + } case MAKE_CLOSURE: { @@ -2577,7 +2597,7 @@ PyObject * PyEval_EvalCodeEx(PyCodeObject *co, PyObject *globals, PyObject *locals, PyObject **args, int argcount, PyObject **kws, int kwcount, - PyObject **defs, int defcount, PyObject *closure) + PyObject **defs, int defcount, PyObject *kwdefs, PyObject *closure) { register PyFrameObject *f; register PyObject *retval = NULL; @@ -2601,6 +2621,7 @@ freevars = f->f_localsplus + co->co_nlocals; if (co->co_argcount > 0 || + co->co_kwonlyargcount > 0 || co->co_flags & (CO_VARARGS | CO_VARKEYWORDS)) { int i; int n = argcount; @@ -2609,7 +2630,7 @@ kwdict = PyDict_New(); if (kwdict == NULL) goto fail; - i = co->co_argcount; + i = co->co_argcount + co->co_kwonlyargcount; if (co->co_flags & CO_VARARGS) i++; SETLOCAL(i, kwdict); @@ -2618,7 +2639,7 @@ if (!(co->co_flags & CO_VARARGS)) { PyErr_Format(PyExc_TypeError, "%.200s() takes %s %d " - "%sargument%s (%d given)", + "%spositional argument%s (%d given)", PyString_AsString(co->co_name), defcount ? "at most" : "exactly", co->co_argcount, @@ -2638,7 +2659,7 @@ u = PyTuple_New(argcount - n); if (u == NULL) goto fail; - SETLOCAL(co->co_argcount, u); + SETLOCAL(co->co_argcount + co->co_kwonlyargcount, u); for (i = n; i < argcount; i++) { x = args[i]; Py_INCREF(x); @@ -2656,7 +2677,9 @@ goto fail; } /* XXX slow -- speed up using dictionary? */ - for (j = 0; j < co->co_argcount; j++) { + for (j = 0; + j < co->co_argcount + co->co_kwonlyargcount; + j++) { PyObject *nm = PyTuple_GET_ITEM( co->co_varnames, j); int cmp = PyObject_RichCompareBool( @@ -2669,7 +2692,7 @@ /* Check errors from Compare */ if (PyErr_Occurred()) goto fail; - if (j >= co->co_argcount) { + if (j >= co->co_argcount + co->co_kwonlyargcount) { if (kwdict == NULL) { PyErr_Format(PyExc_TypeError, "%.200s() got an unexpected " @@ -2694,13 +2717,38 @@ SETLOCAL(j, value); } } + if (co->co_kwonlyargcount > 0) { + for (i = co->co_argcount; + i < co->co_argcount + co->co_kwonlyargcount; + i++) { + if (GETLOCAL(i) != NULL) + continue; + PyObject *name = + PyTuple_GET_ITEM(co->co_varnames, i); + PyObject *def = NULL; + if (kwdefs != NULL) + def = PyDict_GetItem(kwdefs, name); + if (def != NULL) { + Py_INCREF(def); + SETLOCAL(i, def); + continue; + } + PyErr_Format(PyExc_TypeError, + "%.200s() needs " + "keyword only argument %s", + PyString_AsString(co->co_name), + PyString_AsString(name)); + goto fail; + } + } if (argcount < co->co_argcount) { int m = co->co_argcount - defcount; for (i = argcount; i < m; i++) { if (GETLOCAL(i) == NULL) { PyErr_Format(PyExc_TypeError, "%.200s() takes %s %d " - "%sargument%s (%d given)", + "%spositional argument%s " + "(%d given)", PyString_AsString(co->co_name), ((co->co_flags & CO_VARARGS) || defcount) ? "at least" @@ -3565,12 +3613,14 @@ PyCodeObject *co = (PyCodeObject *)PyFunction_GET_CODE(func); PyObject *globals = PyFunction_GET_GLOBALS(func); PyObject *argdefs = PyFunction_GET_DEFAULTS(func); + PyObject *kwdefs = PyFunction_GET_KW_DEFAULTS(func); PyObject **d = NULL; int nd = 0; PCALL(PCALL_FUNCTION); PCALL(PCALL_FAST_FUNCTION); - if (argdefs == NULL && co->co_argcount == n && nk==0 && + if (argdefs == NULL && co->co_argcount == n && + co->co_kwonlyargcount == 0 && nk==0 && co->co_flags == (CO_OPTIMIZED | CO_NEWLOCALS | CO_NOFREE)) { PyFrameObject *f; PyObject *retval = NULL; @@ -3608,7 +3658,7 @@ } return PyEval_EvalCodeEx(co, globals, (PyObject *)NULL, (*pp_stack)-n, na, - (*pp_stack)-2*nk, nk, d, nd, + (*pp_stack)-2*nk, nk, d, nd, kwdefs, PyFunction_GET_CLOSURE(func)); } Modified: python/branches/p3yk/Python/compile.c ============================================================================== --- python/branches/p3yk/Python/compile.c (original) +++ python/branches/p3yk/Python/compile.c Sat Oct 28 01:31:49 2006 @@ -115,6 +115,7 @@ PyObject *u_private; /* for private name mangling */ int u_argcount; /* number of arguments for block */ + int u_kwonlyargcount; /* number of keyword only arguments for block */ /* Pointer to the most recently allocated block. By following b_list members, you can reach all early allocated blocks. */ basicblock *u_blocks; @@ -494,6 +495,7 @@ } memset(u, 0, sizeof(struct compiler_unit)); u->u_argcount = 0; + u->u_kwonlyargcount = 0; u->u_ste = PySymtable_Lookup(c->c_st, key); if (!u->u_ste) { compiler_unit_free(u); @@ -896,9 +898,9 @@ return -NARGS(oparg)-1; case CALL_FUNCTION_VAR_KW: return -NARGS(oparg)-2; -#undef NARGS case MAKE_FUNCTION: - return -oparg; + return -NARGS(oparg); +#undef NARGS case BUILD_SLICE: if (oparg == 3) return -2; @@ -1347,6 +1349,25 @@ } static int +compiler_visit_kwonlydefaults(struct compiler *c, asdl_seq *kwonlyargs, + asdl_seq *kw_defaults) +{ + int i, default_count = 0; + for (i = 0; i < asdl_seq_LEN(kwonlyargs); i++) { + expr_ty arg = asdl_seq_GET(kwonlyargs, i); + expr_ty default_ = asdl_seq_GET(kw_defaults, i); + if (default_) { + ADDOP_O(c, LOAD_CONST, arg->v.Name.id, consts); + if (!compiler_visit_expr(c, default_)) { + return -1; + } + default_count++; + } + } + return default_count; +} + +static int compiler_function(struct compiler *c, stmt_ty s) { PyCodeObject *co; @@ -1354,14 +1375,22 @@ arguments_ty args = s->v.FunctionDef.args; asdl_seq* decos = s->v.FunctionDef.decorators; stmt_ty st; - int i, n, docstring; + int i, n, docstring, kw_default_count = 0, arglength; assert(s->kind == FunctionDef_kind); if (!compiler_decorators(c, decos)) return 0; + if (args->kwonlyargs) { + int res = compiler_visit_kwonlydefaults(c, args->kwonlyargs, + args->kw_defaults); + if (res < 0) + return 0; + kw_default_count = res; + } if (args->defaults) VISIT_SEQ(c, expr, args->defaults); + if (!compiler_enter_scope(c, s->v.FunctionDef.name, (void *)s, s->lineno)) return 0; @@ -1379,6 +1408,7 @@ compiler_arguments(c, args); c->u->u_argcount = asdl_seq_LEN(args->args); + c->u->u_kwonlyargcount = asdl_seq_LEN(args->kwonlyargs); n = asdl_seq_LEN(s->v.FunctionDef.body); /* if there was a docstring, we need to skip the first statement */ for (i = docstring; i < n; i++) { @@ -1390,7 +1420,9 @@ if (co == NULL) return 0; - compiler_make_closure(c, co, asdl_seq_LEN(args->defaults)); + arglength = asdl_seq_LEN(args->defaults); + arglength |= kw_default_count << 8; + compiler_make_closure(c, co, arglength); Py_DECREF(co); for (i = 0; i < asdl_seq_LEN(decos); i++) { @@ -1485,6 +1517,7 @@ { PyCodeObject *co; static identifier name; + int kw_default_count = 0, arglength; arguments_ty args = e->v.Lambda.args; assert(e->kind == Lambda_kind); @@ -1494,6 +1527,12 @@ return 0; } + if (args->kwonlyargs) { + int res = compiler_visit_kwonlydefaults(c, args->kwonlyargs, + args->kw_defaults); + if (res < 0) return 0; + kw_default_count = res; + } if (args->defaults) VISIT_SEQ(c, expr, args->defaults); if (!compiler_enter_scope(c, name, (void *)e, e->lineno)) @@ -1503,6 +1542,7 @@ compiler_arguments(c, args); c->u->u_argcount = asdl_seq_LEN(args->args); + c->u->u_kwonlyargcount = asdl_seq_LEN(args->kwonlyargs); VISIT_IN_SCOPE(c, expr, e->v.Lambda.body); ADDOP_IN_SCOPE(c, RETURN_VALUE); co = assemble(c, 1); @@ -1510,7 +1550,9 @@ if (co == NULL) return 0; - compiler_make_closure(c, co, asdl_seq_LEN(args->defaults)); + arglength = asdl_seq_LEN(args->defaults); + arglength |= kw_default_count << 8; + compiler_make_closure(c, co, arglength); Py_DECREF(co); return 1; @@ -3791,7 +3833,8 @@ Py_DECREF(consts); consts = tmp; - co = PyCode_New(c->u->u_argcount, nlocals, stackdepth(c), flags, + co = PyCode_New(c->u->u_argcount, c->u->u_kwonlyargcount, + nlocals, stackdepth(c), flags, bytecode, consts, names, varnames, freevars, cellvars, filename, c->u->u_name, Modified: python/branches/p3yk/Python/graminit.c ============================================================================== --- python/branches/p3yk/Python/graminit.c (original) +++ python/branches/p3yk/Python/graminit.c Sat Oct 28 01:31:49 2006 @@ -146,14 +146,16 @@ {27, 5}, {0, 1}, }; -static arc arcs_7_2[1] = { +static arc arcs_7_2[3] = { {19, 6}, + {27, 7}, + {0, 2}, }; static arc arcs_7_3[1] = { - {19, 7}, + {19, 8}, }; static arc arcs_7_4[1] = { - {26, 8}, + {26, 9}, }; static arc arcs_7_5[4] = { {24, 1}, @@ -162,30 +164,41 @@ {0, 5}, }; static arc arcs_7_6[2] = { - {27, 9}, + {27, 7}, {0, 6}, }; -static arc arcs_7_7[1] = { - {0, 7}, +static arc arcs_7_7[2] = { + {19, 10}, + {29, 3}, }; -static arc arcs_7_8[2] = { - {27, 5}, +static arc arcs_7_8[1] = { {0, 8}, }; -static arc arcs_7_9[1] = { - {29, 3}, +static arc arcs_7_9[2] = { + {27, 5}, + {0, 9}, +}; +static arc arcs_7_10[3] = { + {27, 7}, + {25, 11}, + {0, 10}, +}; +static arc arcs_7_11[1] = { + {26, 6}, }; -static state states_7[10] = { +static state states_7[12] = { {3, arcs_7_0}, {3, arcs_7_1}, - {1, arcs_7_2}, + {3, arcs_7_2}, {1, arcs_7_3}, {1, arcs_7_4}, {4, arcs_7_5}, {2, arcs_7_6}, - {1, arcs_7_7}, - {2, arcs_7_8}, - {1, arcs_7_9}, + {2, arcs_7_7}, + {1, arcs_7_8}, + {2, arcs_7_9}, + {3, arcs_7_10}, + {1, arcs_7_11}, }; static arc arcs_8_0[2] = { {19, 1}, @@ -1766,7 +1779,7 @@ "\000\010\004\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"}, {262, "parameters", 0, 4, states_6, "\000\040\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"}, - {263, "varargslist", 0, 10, states_7, + {263, "varargslist", 0, 12, states_7, "\000\040\010\060\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"}, {264, "fpdef", 0, 4, states_8, "\000\040\010\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"}, Modified: python/branches/p3yk/Python/import.c ============================================================================== --- python/branches/p3yk/Python/import.c (original) +++ python/branches/p3yk/Python/import.c Sat Oct 28 01:31:49 2006 @@ -67,9 +67,10 @@ Python 3000: 3000 3010 (removed UNARY_CONVERT) 3020 (added BUILD_SET) + 3030 (added keyword-only parameters) . */ -#define MAGIC (3020 | ((long)'\r'<<16) | ((long)'\n'<<24)) +#define MAGIC (3030 | ((long)'\r'<<16) | ((long)'\n'<<24)) /* Magic word as global; note that _PyImport_Init() can change the value of this global to accommodate for alterations of how the Modified: python/branches/p3yk/Python/marshal.c ============================================================================== --- python/branches/p3yk/Python/marshal.c (original) +++ python/branches/p3yk/Python/marshal.c Sat Oct 28 01:31:49 2006 @@ -349,6 +349,7 @@ PyCodeObject *co = (PyCodeObject *)v; w_byte(TYPE_CODE, p); w_long(co->co_argcount, p); + w_long(co->co_kwonlyargcount, p); w_long(co->co_nlocals, p); w_long(co->co_stacksize, p); w_long(co->co_flags, p); @@ -815,6 +816,7 @@ } else { int argcount; + int kwonlyargcount; int nlocals; int stacksize; int flags; @@ -832,6 +834,7 @@ v = NULL; argcount = r_long(p); + kwonlyargcount = r_long(p); nlocals = r_long(p); stacksize = r_long(p); flags = r_long(p); @@ -865,7 +868,8 @@ goto code_error; v = (PyObject *) PyCode_New( - argcount, nlocals, stacksize, flags, + argcount, kwonlyargcount, + nlocals, stacksize, flags, code, consts, names, varnames, freevars, cellvars, filename, name, firstlineno, lnotab); Modified: python/branches/p3yk/Python/symtable.c ============================================================================== --- python/branches/p3yk/Python/symtable.c (original) +++ python/branches/p3yk/Python/symtable.c Sat Oct 28 01:31:49 2006 @@ -893,6 +893,17 @@ } \ } +#define VISIT_KWONLYDEFAULTS(ST, KW_DEFAULTS) { \ + int i = 0; \ + asdl_seq *seq = (KW_DEFAULTS); /* avoid variable capture */ \ + for (i = 0; i < asdl_seq_LEN(seq); i++) { \ + expr_ty elt = (expr_ty)asdl_seq_GET(seq, i); \ + if (!elt) continue; /* can be NULL */ \ + if (!symtable_visit_expr((ST), elt)) \ + return 0; \ + } \ +} + static int symtable_new_tmpname(struct symtable *st) { @@ -910,6 +921,8 @@ return 1; } + + static int symtable_visit_stmt(struct symtable *st, stmt_ty s) { @@ -919,6 +932,9 @@ return 0; if (s->v.FunctionDef.args->defaults) VISIT_SEQ(st, expr, s->v.FunctionDef.args->defaults); + if (s->v.FunctionDef.args->kw_defaults) + VISIT_KWONLYDEFAULTS(st, + s->v.FunctionDef.args->kw_defaults); if (s->v.FunctionDef.decorators) VISIT_SEQ(st, expr, s->v.FunctionDef.decorators); if (!symtable_enter_block(st, s->v.FunctionDef.name, @@ -1262,6 +1278,8 @@ */ if (a->args && !symtable_visit_params(st, a->args, 1)) return 0; + if (a->kwonlyargs && !symtable_visit_params(st, a->kwonlyargs, 1)) + return 0; if (a->vararg) { if (!symtable_add_def(st, a->vararg, DEF_PARAM)) return 0; Modified: python/branches/p3yk/Tools/compiler/ast.txt ============================================================================== --- python/branches/p3yk/Tools/compiler/ast.txt (original) +++ python/branches/p3yk/Tools/compiler/ast.txt Sat Oct 28 01:31:49 2006 @@ -12,8 +12,8 @@ Module: doc*, node Stmt: nodes! Decorators: nodes! -Function: decorators&, name*, argnames*, defaults!, flags*, doc*, code -Lambda: argnames*, defaults!, flags*, code +Function: decorators&, name*, argnames*, defaults!, kwonlyargs*, flags*, doc*, code +Lambda: argnames*, defaults!, kwonlyargs*, flags*, code Class: name*, bases!, doc*, code Pass: Break: @@ -97,6 +97,7 @@ init(GenExpr): self.argnames = ['.0'] self.varargs = self.kwargs = None + self.kwonlyargs = () init(GenExprFor): self.is_outmost = False