Skip to content

Commit b1d3eaa

Browse files
committed
Set function __doc__ even when created via constructor
1 parent 4416be0 commit b1d3eaa

File tree

4 files changed

+62
-16
lines changed

4 files changed

+62
-16
lines changed

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

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved.
1+
# Copyright (c) 2018, 2024, 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
@@ -278,4 +278,20 @@ def cached_func():
278278

279279
type_entity = getattr(sys.modules['functools'], '_lru_cache_wrapper')
280280
assert isinstance(cached_func, type_entity), "lru_cache should not be using the python-based version"
281-
281+
282+
283+
def test_no_docstring():
284+
def no_doc():
285+
pass
286+
287+
assert no_doc.__doc__ is None
288+
289+
290+
def test_docstring_via_type_contructor():
291+
def foo():
292+
"""my doc"""
293+
return 1
294+
295+
assert foo.__doc__ == 'my doc'
296+
foo2 = type(foo)(foo.__code__, foo.__globals__, foo.__name__, foo.__defaults__, foo.__closure__)
297+
assert foo2.__doc__ == 'my doc'

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/function/FunctionBuiltins.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828

2929
import static com.oracle.graal.python.nodes.SpecialAttributeNames.J___CODE__;
3030
import static com.oracle.graal.python.nodes.SpecialAttributeNames.J___DEFAULTS__;
31+
import static com.oracle.graal.python.nodes.SpecialAttributeNames.J___DOC__;
3132
import static com.oracle.graal.python.nodes.SpecialAttributeNames.J___KWDEFAULTS__;
3233
import static com.oracle.graal.python.nodes.SpecialAttributeNames.J___NAME__;
3334
import static com.oracle.graal.python.nodes.SpecialAttributeNames.J___QUALNAME__;
@@ -303,4 +304,19 @@ static Object setCode(PFunction self, PCode code,
303304
return PNone.NONE;
304305
}
305306
}
307+
308+
@Builtin(name = J___DOC__, minNumOfPositionalArgs = 1, maxNumOfPositionalArgs = 2, isGetter = true, isSetter = true)
309+
@GenerateNodeFactory
310+
abstract static class DocNode extends PythonBinaryBuiltinNode {
311+
@Specialization(guards = "isNoValue(none)")
312+
Object get(PFunction self, @SuppressWarnings("unused") Object none) {
313+
return self.getDoc();
314+
}
315+
316+
@Specialization(guards = "!isNoValue(value)")
317+
Object set(PFunction self, Object value) {
318+
self.setDoc(value);
319+
return PNone.NONE;
320+
}
321+
}
306322
}

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/function/PFunction.java

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2017, 2023, Oracle and/or its affiliates.
2+
* Copyright (c) 2017, 2024, Oracle and/or its affiliates.
33
* Copyright (c) 2013, Regents of the University of California
44
*
55
* All rights reserved.
@@ -27,10 +27,13 @@
2727

2828
import com.oracle.graal.python.PythonLanguage;
2929
import com.oracle.graal.python.builtins.PythonBuiltinClassType;
30+
import com.oracle.graal.python.builtins.objects.PNone;
3031
import com.oracle.graal.python.builtins.objects.cell.PCell;
3132
import com.oracle.graal.python.builtins.objects.code.CodeNodes.GetCodeCallTargetNode;
3233
import com.oracle.graal.python.builtins.objects.code.PCode;
3334
import com.oracle.graal.python.builtins.objects.object.PythonObject;
35+
import com.oracle.graal.python.compiler.CodeUnit;
36+
import com.oracle.graal.python.lib.PyUnicodeCheckNode;
3437
import com.oracle.graal.python.nodes.PRootNode;
3538
import com.oracle.graal.python.nodes.builtins.FunctionNodes.GetCallTargetNode;
3639
import com.oracle.graal.python.runtime.GilNode;
@@ -69,6 +72,7 @@ public final class PFunction extends PythonObject {
6972
private Object[] defaultValues;
7073
@CompilationFinal(dimensions = 1) private PKeyword[] finalKwDefaultValues;
7174
private PKeyword[] kwDefaultValues;
75+
private Object doc;
7276

7377
public PFunction(PythonLanguage lang, TruffleString name, TruffleString qualname, PCode code, PythonObject globals, PCell[] closure) {
7478
this(lang, name, qualname, code, globals, PythonUtils.EMPTY_OBJECT_ARRAY, PKeyword.EMPTY_KEYWORDS, closure);
@@ -145,6 +149,27 @@ public boolean forceSplitDirectCalls() {
145149
return forceSplitDirectCalls;
146150
}
147151

152+
public Object getDoc() {
153+
if (CompilerDirectives.injectBranchProbability(CompilerDirectives.SLOWPATH_PROBABILITY, doc == null)) {
154+
extractDoc();
155+
}
156+
return doc;
157+
}
158+
159+
public void setDoc(Object doc) {
160+
this.doc = doc;
161+
}
162+
163+
@TruffleBoundary
164+
private void extractDoc() {
165+
CodeUnit co = getCode().getCodeUnit();
166+
if (co != null && co.constants.length > 0 && PyUnicodeCheckNode.executeUncached(co.constants[0])) {
167+
doc = co.constants[0];
168+
} else {
169+
doc = PNone.NONE;
170+
}
171+
}
172+
148173
@Override
149174
public final String toString() {
150175
CompilerAsserts.neverPartOfCompilation();

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/MakeFunctionNode.java

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@
4141
package com.oracle.graal.python.nodes.bytecode;
4242

4343
import static com.oracle.graal.python.nodes.SpecialAttributeNames.T___ANNOTATIONS__;
44-
import static com.oracle.graal.python.nodes.SpecialAttributeNames.T___DOC__;
4544

4645
import com.oracle.graal.python.PythonLanguage;
4746
import com.oracle.graal.python.builtins.objects.cell.PCell;
@@ -64,25 +63,22 @@
6463
import com.oracle.truffle.api.dsl.Specialization;
6564
import com.oracle.truffle.api.frame.VirtualFrame;
6665
import com.oracle.truffle.api.source.Source;
67-
import com.oracle.truffle.api.strings.TruffleString;
6866

6967
public abstract class MakeFunctionNode extends PNodeWithContext {
7068
private final RootCallTarget callTarget;
7169
private final CodeUnit code;
7270
private final Signature signature;
73-
private final TruffleString doc;
7471
@CompilationFinal private PCode cachedCode;
7572

7673
private final Assumption sharedCodeStableAssumption = Truffle.getRuntime().createAssumption("shared code stable assumption");
7774
private final Assumption sharedDefaultsStableAssumption = Truffle.getRuntime().createAssumption("shared defaults stable assumption");
7875

7976
public abstract int execute(VirtualFrame frame, Object globals, int initialStackTop, int flags);
8077

81-
public MakeFunctionNode(RootCallTarget callTarget, CodeUnit code, Signature signature, TruffleString doc) {
78+
public MakeFunctionNode(RootCallTarget callTarget, CodeUnit code, Signature signature) {
8279
this.callTarget = callTarget;
8380
this.code = code;
8481
this.signature = signature;
85-
this.doc = doc;
8682
}
8783

8884
@Specialization
@@ -142,9 +138,6 @@ int makeFunction(VirtualFrame frame, Object globals, int initialStackTop, int fl
142138
if (annotations != null) {
143139
writeAttrNode.execute(function, T___ANNOTATIONS__, annotations);
144140
}
145-
if (doc != null) {
146-
writeAttrNode.execute(function, T___DOC__, doc);
147-
}
148141

149142
frame.setObject(++stackTop, function);
150143
return stackTop;
@@ -159,11 +152,7 @@ public static MakeFunctionNode create(PythonLanguage language, CodeUnit code, So
159152
} else {
160153
callTarget = bytecodeRootNode.getCallTarget();
161154
}
162-
TruffleString doc = null;
163-
if (code.constants.length > 0 && code.constants[0] instanceof TruffleString) {
164-
doc = (TruffleString) code.constants[0];
165-
}
166-
return MakeFunctionNodeGen.create(callTarget, code, bytecodeRootNode.getSignature(), doc);
155+
return MakeFunctionNodeGen.create(callTarget, code, bytecodeRootNode.getSignature());
167156
}
168157

169158
public RootCallTarget getCallTarget() {

0 commit comments

Comments
 (0)