Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions graalpython/com.oracle.graal.python.test/src/tests/test_type.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ class MyStr(str):
MyStr("x"): 42
})
assert any(type(k) == MyStr for k in A.__dict__.keys())
assert any(type(k) == MyStr for k in type.__dir__(A))


def test_mro():
Expand All @@ -186,6 +187,35 @@ class C:
assert dir(C()) == sorted(dir(C()))


def test_type_dir_many_string_attributes():
base_attributes = {f"base_{i}": i for i in range(200)}
attributes = {f"attribute_{i}": i for i in range(1000)}
base = type("Base", (), base_attributes)
cls = type("ManyAttributes", (base,), attributes)
del cls.attribute_5
expected = set(base.__dict__) | set(cls.__dict__) | set(object.__dict__)
assert set(type.__dir__(cls)) == expected
assert dir(cls) == sorted(expected)


def test_type_dir_custom_mappingproxy_falls_back():
lookups = []

class Meta(type):
@property
def __dict__(self):
lookups.append(self)
return {"custom": 1}

class C(metaclass=Meta):
hidden_by_metaclass = 1

result = type.__dir__(C)
assert "custom" in result
assert "hidden_by_metaclass" not in result
assert lookups == [C]


def test_isinstance_non_type():
import typing
assert isinstance(1, typing.AbstractSet) is False
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@
import static com.oracle.graal.python.util.PythonUtils.tsLiteral;

import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.List;

import com.oracle.graal.python.PythonLanguage;
Expand Down Expand Up @@ -180,6 +181,7 @@
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.object.DynamicObject;
import com.oracle.truffle.api.profiles.BranchProfile;
import com.oracle.truffle.api.profiles.InlinedBranchProfile;
import com.oracle.truffle.api.profiles.InlinedConditionProfile;
Expand Down Expand Up @@ -1242,9 +1244,18 @@ public abstract static class DirNode extends PythonUnaryBuiltinNode {
@Specialization
static PList dir(VirtualFrame frame, Object klass,
@Bind Node inliningTarget,
@Bind PythonLanguage language,
@Bind PythonContext context,
@Cached ConstructListNode constructListNode,
@Cached InlinedBranchProfile slowPathBranch,
@Cached("createFor($node)") BoundaryCallData boundaryCallData) {
PSet names = PFactory.createSet(PythonLanguage.get(inliningTarget));
Object[] fastNames = dirFast(language, context, klass);
if (fastNames != null) {
return PFactory.createList(language, fastNames);
}

slowPathBranch.enter(inliningTarget);
PSet names = PFactory.createSet(language);
Object state = BoundaryCallContext.enter(frame, boundaryCallData);
try {
dir(names, klass);
Expand All @@ -1254,6 +1265,56 @@ static PList dir(VirtualFrame frame, Object klass,
return constructListNode.execute(frame, names);
}

@TruffleBoundary
private static Object[] dirFast(PythonLanguage language, PythonContext context, Object klass) {
if (!isFastPathEligible(context, klass)) {
return null;
}
LinkedHashSet<TruffleString> names = new LinkedHashSet<>();
collectDynamicObjectStorageKeys(names, context, klass);
return names.toArray();
}

private static boolean isFastPathEligible(PythonContext context, Object klass) {
PythonManagedClass managedClass = asManagedClass(context, klass);
if (managedClass == null || PGuards.hasMaterializedDict(managedClass.getShape())) {
return false;
}
for (PythonAbstractClass base : managedClass.getBaseClasses()) {
if (!isFastPathEligible(context, base)) {
return false;
}
}
return true;
}

private static PythonManagedClass asManagedClass(PythonContext context, Object klass) {
if (GetClassNode.executeUncached(klass) != PythonBuiltinClassType.PythonClass) {
return null;
}
if (klass instanceof PythonManagedClass pythonManagedClass) {
return pythonManagedClass;
} else if (klass instanceof PythonBuiltinClassType builtinClassType) {
return context.lookupType(builtinClassType);
}
return null;
}

private static void collectDynamicObjectStorageKeys(LinkedHashSet<TruffleString> names, PythonContext context, Object klass) {
PythonManagedClass managedClass = asManagedClass(context, klass);
assert managedClass != null && !PGuards.hasMaterializedDict(managedClass.getShape());
DynamicObject.GetNode getNode = DynamicObject.GetNode.getUncached();
for (Object key : DynamicObject.GetKeyArrayNode.getUncached().execute(managedClass)) {
if (key instanceof TruffleString stringKey && getNode.execute(managedClass, stringKey, NO_VALUE) != NO_VALUE) {
names.add(stringKey);
}
}

for (PythonAbstractClass base : managedClass.getBaseClasses()) {
collectDynamicObjectStorageKeys(names, context, base);
}
}

@TruffleBoundary
public static void dir(PSet names, Object klass) {
Object ns = PyObjectLookupAttr.executeUncached(klass, T___DICT__);
Expand Down
Loading