Skip to content

Commit 496ff18

Browse files
committed
[GR-13908] [GR-13738] Function defaults and kwdefaults should be writeable.
PullRequest: graalpython/406
2 parents 60257b9 + 51e7708 commit 496ff18

File tree

142 files changed

+3255
-2942
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

142 files changed

+3255
-2942
lines changed

graalpython/com.oracle.graal.python.shell/src/com/oracle/graal/python/shell/GraalPythonMain.java

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ public static void main(String[] args) {
7171
private boolean noSite = false;
7272
private boolean stdinIsInteractive = System.console() != null;
7373
private boolean runLLI = false;
74+
private boolean unbufferedIO = false;
7475
private VersionAction versionAction = VersionAction.None;
7576
private String sulongLibraryPath = null;
7677
private List<String> givenArguments;
@@ -89,7 +90,6 @@ protected List<String> preprocessArguments(List<String> givenArgs, Map<String, S
8990
String arg = arguments.get(i);
9091
switch (arg) {
9192
case "-B":
92-
System.out.println("Warning: " + arg + " does nothing on GraalPython.");
9393
break;
9494
case "-c":
9595
i += 1;
@@ -122,7 +122,7 @@ protected List<String> preprocessArguments(List<String> givenArgs, Map<String, S
122122
break;
123123
case "-O":
124124
case "-OO":
125-
System.out.println("Warning: " + arg + " does nothing on GraalPython.");
125+
case "-R":
126126
break;
127127
case "-q":
128128
quietFlag = true;
@@ -184,15 +184,20 @@ protected List<String> preprocessArguments(List<String> givenArgs, Map<String, S
184184
inputArgs.remove("-compile-truffle-immediately");
185185
break;
186186
case "-u":
187-
// TODO we currently don't support this option, but needs to be consumed
188-
// due pip/wheel installer.
187+
unbufferedIO = true;
189188
break;
190189
default:
191190
if (!arg.startsWith("-")) {
192191
inputFile = arg;
193192
programArgs.add(inputFile);
194193
break;
194+
} else if (!arg.startsWith("--") && arg.length() > 2) {
195+
// short arguments can be given together
196+
for (String optionChar : arg.substring(1).split("")) {
197+
arguments.add(i + 1, "-" + optionChar);
198+
}
195199
} else {
200+
// possibly a polyglot argument
196201
unrecognized.add(arg);
197202
}
198203
}
@@ -340,6 +345,7 @@ protected void launch(Builder contextBuilder) {
340345
inspectFlag = inspectFlag || System.getenv("PYTHONINSPECT") != null;
341346
noUserSite = noUserSite || System.getenv("PYTHONNOUSERSITE") != null;
342347
verboseFlag = verboseFlag || System.getenv("PYTHONVERBOSE") != null;
348+
unbufferedIO = unbufferedIO || System.getenv("PYTHONUNBUFFERED") != null;
343349
}
344350

345351
// The unlikely separator is used because options need to be strings. See
@@ -359,6 +365,7 @@ protected void launch(Builder contextBuilder) {
359365
contextBuilder.option("python.NoUserSiteFlag", Boolean.toString(noUserSite));
360366
contextBuilder.option("python.NoSiteFlag", Boolean.toString(noSite));
361367
contextBuilder.option("python.IgnoreEnvironmentFlag", Boolean.toString(ignoreEnv));
368+
contextBuilder.option("python.UnbufferedIO", Boolean.toString(unbufferedIO));
362369

363370
sulongLibraryPath = System.getenv("SULONG_LIBRARY_PATH");
364371
if (sulongLibraryPath != null) {
@@ -492,29 +499,31 @@ protected String getLanguageId() {
492499
protected void printHelp(OptionCategory maxCategory) {
493500
print("usage: python [option] ... (-c cmd | file) [arg] ...\n" +
494501
"Options and arguments (and corresponding environment variables):\n" +
495-
"-B : don't write .py[co] files on import; also PYTHONDONTWRITEBYTECODE=x\n" +
502+
"-B : on CPython, this disables writing .py[co] files on import;\n" +
503+
" GraalPython does not use bytecode, and thus this flag has no effect\n" +
496504
"-c cmd : program passed in as string (terminates option list)\n" +
497505
// "-d : debug output from parser; also PYTHONDEBUG=x\n" +
498506
"-E : ignore PYTHON* environment variables (such as PYTHONPATH)\n" +
499507
"-h : print this help message and exit (also --help)\n" +
500508
"-i : inspect interactively after running script; forces a prompt even\n" +
501509
" if stdin does not appear to be a terminal; also PYTHONINSPECT=x\n" +
502510
"-m mod : run library module as a script (terminates option list)\n" +
503-
"-O : optimize generated bytecode slightly; also PYTHONOPTIMIZE=x\n" +
504-
"-OO : remove doc-strings in addition to the -O optimizations\n" +
505-
// "-R : use a pseudo-random salt to make hash() values of various types
506-
// be\n" +
507-
// " unpredictable between separate invocations of the interpreter, as\n" +
508-
// " a defense against denial-of-service attacks\n" +
511+
"-O : on CPython, this optimizes generated bytecode slightly;\n" +
512+
" GraalPython does not use bytecode, and thus this flag has no effect\n" +
513+
"-OO : remove doc-strings in addition to the -O optimizations;\n" +
514+
" GraalPython does not use bytecode, and thus this flag has no effect\n" +
515+
"-R : on CPython, this enables the use of a pseudo-random salt to make\n" +
516+
" hash()values of various types be unpredictable between separate\n" +
517+
" invocations of the interpreter, as a defense against denial-of-service\n" +
518+
" attacks; GraalPython always enables this and the flag has no effect.\n" +
509519
// "-Q arg : division options: -Qold (default), -Qwarn, -Qwarnall, -Qnew\n"
510520
// +
511521
"-q : don't print version and copyright messages on interactive startup\n" +
512522
"-s : don't add user site directory to sys.path; also PYTHONNOUSERSITE\n" +
513523
"-S : don't imply 'import site' on initialization\n" +
514524
// "-t : issue warnings about inconsistent tab usage (-tt: issue errors)\n"
515525
// +
516-
// "-u : unbuffered binary stdout and stderr; also PYTHONUNBUFFERED=x\n" +
517-
// " see man page for details on internal buffering relating to '-u'\n" +
526+
"-u : unbuffered binary stdout and stderr; also PYTHONUNBUFFERED=x\n" +
518527
"-v : verbose (trace import statements); also PYTHONVERBOSE=x\n" +
519528
" can be supplied multiple times to increase verbosity\n" +
520529
"-V : print the Python version number and exit (also --version)\n" +

graalpython/com.oracle.graal.python.test/src/graalpytest.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ def __enter__(self):
201201

202202
def __exit__(self, exc_type, exc, traceback):
203203
if not exc_type:
204-
assert False, "expected '%r' to raise '%r'" % (self.function, exc_type)
204+
assert False, "expected '%r' to raise '%r'" % (self.function, self.exc_type)
205205
elif self.exc_type in exc_type.mro():
206206
self.exception = exc
207207
return True

graalpython/com.oracle.graal.python.test/src/tests/test_functions.py

Lines changed: 132 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
1+
# Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
22
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
33
#
44
# The Universal Permissive License (UPL), Version 1.0
@@ -105,6 +105,134 @@ def test_constructor():
105105
import types
106106
func_copy = types.FunctionType(f.__code__, f.__globals__, f.__name__, f.__defaults__, f.__closure__)
107107

108-
assert func_copy(1, 2) == (1, 2, 10, (), {})
109-
assert func_copy(1, 2, 3) == (1, 2, 3, (), {})
110-
assert func_copy(1, 2, 3, 4, 5, x=2) == (1, 2, 3, (4, 5), {'x': 2})
108+
assert func_copy(1, 2) == (1, 2, 10, (), {}), func_copy(1, 2)
109+
assert func_copy(1, 2, 3) == (1, 2, 3, (), {}), func_copy(1, 2, 3)
110+
assert func_copy(1, 2, 3, 4, 5, x=2) == (1, 2, 3, (4, 5), {'x': 2}), func_copy(1, 2, 3, 4, 5, x=2)
111+
112+
113+
def test_inner_function_with_defaults():
114+
def make_func(sep):
115+
def inner(sep=sep):
116+
return sep
117+
return inner
118+
119+
inner_a = make_func(",")
120+
inner_b = make_func("\t")
121+
assert inner_b() == "\t"
122+
assert inner_a() == ","
123+
124+
125+
def test_inner_function_with_closure():
126+
def make_func(sep):
127+
def inner():
128+
return sep
129+
return inner
130+
131+
inner_a = make_func(",")
132+
inner_b = make_func("\t")
133+
assert inner_a() == ","
134+
assert inner_b() == "\t"
135+
136+
137+
def test_inner_generator_with_defaults():
138+
def make_func(sep):
139+
def inner(sep=sep):
140+
yield sep
141+
return inner
142+
143+
inner_a = make_func(",")()
144+
inner_b = make_func("\t")()
145+
assert next(inner_b) == "\t"
146+
assert next(inner_a) == ","
147+
148+
149+
def test_inner_generator_with_closure():
150+
def make_func(sep):
151+
closure_value = sep
152+
def inner():
153+
yield closure_value
154+
return inner
155+
156+
inner_a = make_func(",")()
157+
inner_b = make_func("\t")()
158+
assert next(inner_b) == "\t"
159+
assert next(inner_a) == ","
160+
161+
162+
def test_function_changes_defaults():
163+
def foo(a):
164+
return a
165+
166+
assert foo.__defaults__ is None
167+
assert foo.__kwdefaults__ is None
168+
assert_raises(TypeError, foo)
169+
170+
foo.__defaults__ = (1,)
171+
assert foo() == 1
172+
173+
foo.__kwdefaults__ = {"a": 12}
174+
assert foo() == 1
175+
176+
foo.__defaults__ = None
177+
assert_raises(TypeError, foo)
178+
179+
180+
def test_function_changes_kwdefaults():
181+
def foo(*args, x=1):
182+
return x
183+
184+
assert foo.__defaults__ is None
185+
assert foo.__kwdefaults__ == {"x": 1}
186+
assert foo() == 1
187+
188+
foo.__kwdefaults__ = {"x": 32}
189+
assert foo() == 32
190+
191+
foo.__kwdefaults__ = None
192+
assert_raises(TypeError, foo)
193+
194+
195+
def test_code_change():
196+
def foo():
197+
return "foo"
198+
199+
def bar(a):
200+
return "bar" + str(a)
201+
202+
assert foo() == "foo"
203+
foo.__code__ = bar.__code__
204+
assert foo(1) == "bar1"
205+
assert_raises(TypeError, foo)
206+
207+
208+
def test_code_marshal_with_freevars():
209+
import marshal
210+
def foo():
211+
x,y = 1,2
212+
def bar():
213+
return x,y
214+
return bar
215+
216+
def baz():
217+
x,y = 2,3
218+
def bar():
219+
return y,x
220+
return bar
221+
222+
foobar_str = marshal.dumps(foo().__code__)
223+
foobar_code = marshal.loads(foobar_str)
224+
assert_raises(TypeError, exec, foobar_code)
225+
226+
bazbar = baz()
227+
assert bazbar() == (3,2)
228+
229+
def assign_code(x, y):
230+
if isinstance(y, type(assign_code)):
231+
x.__code__ = y.__code__
232+
else:
233+
x.__code__ = y
234+
235+
assert_raises(ValueError, assign_code, foo, bazbar)
236+
assert_raises(ValueError, assign_code, foo, foobar_code)
237+
bazbar.__code__ = foobar_code
238+
assert bazbar() == (2,3)

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/PythonLanguage.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@
4242
import com.oracle.graal.python.builtins.objects.function.PArguments;
4343
import com.oracle.graal.python.builtins.objects.function.PBuiltinFunction;
4444
import com.oracle.graal.python.builtins.objects.function.PFunction;
45-
import com.oracle.graal.python.builtins.objects.function.PKeyword;
4645
import com.oracle.graal.python.builtins.objects.method.PBuiltinMethod;
4746
import com.oracle.graal.python.builtins.objects.method.PMethod;
4847
import com.oracle.graal.python.builtins.objects.module.PythonModule;
@@ -446,7 +445,7 @@ protected String toString(PythonContext context, Object value) {
446445
Object[] userArgs = PArguments.create(2);
447446
PArguments.setArgument(userArgs, 0, PNone.NONE);
448447
PArguments.setArgument(userArgs, 1, value);
449-
Object res = InvokeNode.create(reprMethod).execute(null, userArgs, PKeyword.EMPTY_KEYWORDS);
448+
Object res = InvokeNode.create(reprMethod).execute(null, userArgs);
450449
return res.toString();
451450
}
452451

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/Builtin.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2017, 2018, Oracle and/or its affiliates.
2+
* Copyright (c) 2017, 2019, Oracle and/or its affiliates.
33
* Copyright (c) 2013, Regents of the University of California
44
*
55
* All rights reserved.
@@ -39,23 +39,23 @@
3939

4040
PythonBuiltinClassType[] base() default {};
4141

42-
int fixedNumOfPositionalArgs() default 0;
43-
4442
int minNumOfPositionalArgs() default 0;
4543

46-
int maxNumOfPositionalArgs() default 0;
44+
int maxNumOfPositionalArgs() default -1;
4745

4846
boolean isGetter() default false;
4947

5048
boolean isSetter() default false;
5149

5250
boolean takesVarArgs() default false;
5351

52+
boolean varArgsMarker() default false;
53+
5454
boolean takesVarKeywordArgs() default false;
5555

5656
String[] parameterNames() default {};
5757

58-
String[] keywordArguments() default {};
58+
String[] keywordOnlyNames() default {};
5959

6060
boolean isPublic() default true;
6161

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/Python3Core.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@
168168
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
169169
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
170170
import com.oracle.truffle.api.RootCallTarget;
171+
import com.oracle.truffle.api.Truffle;
171172
import com.oracle.truffle.api.TruffleFile;
172173
import com.oracle.truffle.api.TruffleLanguage.Env;
173174
import com.oracle.truffle.api.TruffleOptions;
@@ -629,7 +630,7 @@ private Source getSource(String basename, String prefix) {
629630

630631
private void loadFile(String s, String prefix) {
631632
Source source = getSource(s, prefix);
632-
Supplier<PCode> getCode = () -> factory.createCode((RootNode) getParser().parse(ParserMode.File, this, source, null));
633+
Supplier<PCode> getCode = () -> factory.createCode(Truffle.getRuntime().createCallTarget((RootNode) getParser().parse(ParserMode.File, this, source, null)));
633634
RootCallTarget callTarget = getLanguage().cacheCode(source.getName(), getCode).getRootCallTarget();
634635
PythonModule mod = lookupBuiltinModule(s);
635636
if (mod == null) {

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/PythonBuiltins.java

Lines changed: 9 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828
import static com.oracle.graal.python.nodes.SpecialAttributeNames.__DOC__;
2929
import static com.oracle.graal.python.nodes.SpecialMethodNames.__NEW__;
3030

31-
import java.util.ArrayList;
3231
import java.util.Collections;
3332
import java.util.HashMap;
3433
import java.util.List;
@@ -37,7 +36,6 @@
3736
import java.util.function.BiConsumer;
3837

3938
import com.oracle.graal.python.builtins.objects.PNone;
40-
import com.oracle.graal.python.builtins.objects.function.Arity;
4139
import com.oracle.graal.python.builtins.objects.function.PBuiltinFunction;
4240
import com.oracle.graal.python.builtins.objects.type.PythonBuiltinClass;
4341
import com.oracle.graal.python.nodes.function.BuiltinFunctionRootNode;
@@ -79,14 +77,14 @@ public void initialize(PythonCore core) {
7977
Object builtinDoc = builtin.doc().isEmpty() ? PNone.NONE : builtin.doc();
8078
if (builtin.constructsClass().length > 0) {
8179
assert !builtin.isGetter() && !builtin.isSetter() && !builtin.isClassmethod() && !builtin.isStaticmethod();
82-
PBuiltinFunction newFunc = core.factory().createBuiltinFunction(__NEW__, null, createArity(builtin, declaresExplicitSelf), callTarget);
80+
PBuiltinFunction newFunc = core.factory().createBuiltinFunction(__NEW__, null, numDefaults(builtin), callTarget);
8381
for (PythonBuiltinClassType type : builtin.constructsClass()) {
8482
PythonBuiltinClass builtinClass = core.lookupType(type);
8583
builtinClass.setAttributeUnsafe(__NEW__, newFunc);
8684
builtinClass.setAttribute(__DOC__, builtinDoc);
8785
}
8886
} else {
89-
PBuiltinFunction function = core.factory().createBuiltinFunction(builtin.name(), null, createArity(builtin, declaresExplicitSelf), callTarget);
87+
PBuiltinFunction function = core.factory().createBuiltinFunction(builtin.name(), null, numDefaults(builtin), callTarget);
9088
function.setAttribute(__DOC__, builtinDoc);
9189
BoundBuiltinCallable<?> callable = function;
9290
if (builtin.isGetter() || builtin.isSetter()) {
@@ -122,27 +120,14 @@ private void initializeEachFactoryWith(BiConsumer<NodeFactory<? extends PythonBu
122120
}
123121
}
124122

125-
private static Arity createArity(Builtin builtin, boolean declaresExplicitSelf) {
126-
int minNumPosArgs = builtin.minNumOfPositionalArgs();
127-
int maxNumPosArgs = Math.max(minNumPosArgs, builtin.maxNumOfPositionalArgs());
128-
if (builtin.fixedNumOfPositionalArgs() > 0) {
129-
minNumPosArgs = maxNumPosArgs = builtin.fixedNumOfPositionalArgs();
123+
private static int numDefaults(Builtin builtin) {
124+
String[] parameterNames = builtin.parameterNames();
125+
int maxNumPosArgs = Math.max(builtin.minNumOfPositionalArgs(), parameterNames.length);
126+
if (builtin.maxNumOfPositionalArgs() >= 0) {
127+
maxNumPosArgs = builtin.maxNumOfPositionalArgs();
128+
assert parameterNames.length == 0 : "either give all parameter names explicitly, or define the max number: " + builtin.name();
130129
}
131-
if (!declaresExplicitSelf) {
132-
// if we don't take the explicit self, we still need to accept it by arity
133-
minNumPosArgs++;
134-
maxNumPosArgs++;
135-
}
136-
137-
List<Arity.KeywordName> keywordNames = new ArrayList<>();
138-
for (String keywordArgument : builtin.keywordArguments()) {
139-
keywordNames.add(new Arity.KeywordName(keywordArgument));
140-
// the assumption here is that out Builtins do not take required keyword args,
141-
// all supplied keyword args are before *args, **kwargs
142-
maxNumPosArgs++;
143-
}
144-
145-
return new Arity(builtin.name(), minNumPosArgs, maxNumPosArgs, builtin.takesVarKeywordArgs(), builtin.takesVarArgs(), builtin.parameterNames(), keywordNames.toArray(new Arity.KeywordName[0]));
130+
return maxNumPosArgs - builtin.minNumOfPositionalArgs();
146131
}
147132

148133
private void setBuiltinFunction(String name, BoundBuiltinCallable<?> function) {

0 commit comments

Comments
 (0)