Skip to content
This repository was archived by the owner on Jul 17, 2024. It is now read-only.

feat: Add support for Python 3.12 #27

Merged
merged 6 commits into from
Apr 2, 2024
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
4 changes: 2 additions & 2 deletions .github/workflows/pull_request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -72,13 +72,13 @@ jobs:
distribution: 'temurin'
cache: 'maven'
# Need to install all Python versions in the same run for tox
- name: Python 3.9, Python 3.10, Python 3.11 Setup
- name: Python 3.10, Python 3.11, Python 3.12 Setup
uses: actions/setup-python@v4
with:
python-version: |
3.9
3.10
3.11
3.12
cache: 'pip'
cache-dependency-path: |
**/setup.py
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/sonarcloud.yml
Original file line number Diff line number Diff line change
Expand Up @@ -77,13 +77,13 @@ jobs:
key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
restore-keys: ${{ runner.os }}-m2
# Need to install all Python versions in the same run for tox
- name: Python 3.9, Python 3.10, Python 3.11 Setup
- name: Python 3.10, Python 3.11, Python 3.12 Setup
uses: actions/setup-python@v4
with:
python-version: |
3.9
3.10
3.11
3.12
cache: 'pip'
cache-dependency-path: |
**/setup.py
Expand Down
4 changes: 2 additions & 2 deletions jpyinterpreter/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,9 @@ def run(self):
'org-stubs': 'src/main/resources',
},
test_suite='tests',
python_requires='>=3.9',
python_requires='>=3.10',
install_requires=[
'JPype1>=1.4.1',
'JPype1>=1.5.0',
],
cmdclass={'build_py': FetchDependencies},
package_data={
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
package ai.timefold.jpyinterpreter;

import java.util.Objects;

public enum CompareOp {
LESS_THAN(0, "__lt__"),
LESS_THAN_OR_EQUALS(1, "__le__"),
EQUALS(2, "__eq__"),
NOT_EQUALS(3, "__ne__"),
GREATER_THAN(4, "__gt__"),
GREATER_THAN_OR_EQUALS(5, "__ge__");
LESS_THAN("<", "__lt__"),
LESS_THAN_OR_EQUALS("<=", "__le__"),
EQUALS("==", "__eq__"),
NOT_EQUALS("!=", "__ne__"),
GREATER_THAN(">", "__gt__"),
GREATER_THAN_OR_EQUALS(">=", "__ge__");

public final int id;
public final String id;
public final String dunderMethod;

CompareOp(int id, String dunderMethod) {
CompareOp(String id, String dunderMethod) {
this.id = id;
this.dunderMethod = dunderMethod;
}
Expand All @@ -25,9 +27,9 @@ public static CompareOp getOpForDunderMethod(String dunderMethod) {
throw new IllegalArgumentException("No Op corresponds to dunder method (" + dunderMethod + ")");
}

public static CompareOp getOp(int id) {
public static CompareOp getOp(String id) {
for (CompareOp op : CompareOp.values()) {
if (op.id == id) {
if (Objects.equals(op.id, id)) {
return op;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,36 @@

import ai.timefold.jpyinterpreter.opcodes.descriptor.OpcodeDescriptor;

public record PythonBytecodeInstruction(String opname, int offset, int arg, OptionalInt startsLine,
public record PythonBytecodeInstruction(String opname, int offset, int arg,
String argRepr, OptionalInt startsLine,
boolean isJumpTarget) {
public static PythonBytecodeInstruction atOffset(String opname, int offset) {
return new PythonBytecodeInstruction(opname, offset, 0, OptionalInt.empty(), false);
return new PythonBytecodeInstruction(opname, offset, 0, "", OptionalInt.empty(), false);
}

public static PythonBytecodeInstruction atOffset(OpcodeDescriptor instruction, int offset) {
return atOffset(instruction.name(), offset);
}

public PythonBytecodeInstruction withArg(int newArg) {
return new PythonBytecodeInstruction(opname, offset, newArg, startsLine, isJumpTarget);
return new PythonBytecodeInstruction(opname, offset, newArg, argRepr, startsLine, isJumpTarget);
}

public PythonBytecodeInstruction withArgRepr(String newArgRepr) {
return new PythonBytecodeInstruction(opname, offset, arg, newArgRepr, startsLine, isJumpTarget);
}

public PythonBytecodeInstruction startsLine(int lineNumber) {
return new PythonBytecodeInstruction(opname, offset, arg, OptionalInt.of(lineNumber), isJumpTarget);
return new PythonBytecodeInstruction(opname, offset, arg, argRepr, OptionalInt.of(lineNumber),
isJumpTarget);
}

public PythonBytecodeInstruction withIsJumpTarget(boolean isJumpTarget) {
return new PythonBytecodeInstruction(opname, offset, arg, startsLine, isJumpTarget);
return new PythonBytecodeInstruction(opname, offset, arg, argRepr, startsLine, isJumpTarget);
}

public PythonBytecodeInstruction markAsJumpTarget() {
return new PythonBytecodeInstruction(opname, offset, arg, startsLine, true);
return new PythonBytecodeInstruction(opname, offset, arg, argRepr, startsLine, true);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.stream.Collectors;
Expand All @@ -30,6 +32,7 @@
import ai.timefold.jpyinterpreter.opcodes.OpcodeWithoutSource;
import ai.timefold.jpyinterpreter.opcodes.SelfOpcodeWithoutSource;
import ai.timefold.jpyinterpreter.opcodes.descriptor.GeneratorOpDescriptor;
import ai.timefold.jpyinterpreter.opcodes.variable.LoadFastAndClearOpcode;
import ai.timefold.jpyinterpreter.types.BuiltinTypes;
import ai.timefold.jpyinterpreter.types.PythonLikeFunction;
import ai.timefold.jpyinterpreter.types.PythonLikeType;
Expand Down Expand Up @@ -175,7 +178,7 @@ public static <T> T forceTranslatePythonBytecodeToGenerator(PythonCompiledFuncti
Method methodWithoutGenerics = getFunctionalInterfaceMethod(javaFunctionalInterfaceType);
MethodDescriptor methodDescriptor = new MethodDescriptor(javaFunctionalInterfaceType,
methodWithoutGenerics,
List.of());
Collections.emptyList());
Class<T> compiledClass = forceTranslatePythonBytecodeToGeneratorClass(pythonCompiledFunction, methodDescriptor,
methodWithoutGenerics, false);
return FunctionImplementor.createInstance(new PythonLikeTuple(), new PythonLikeDict(),
Expand Down Expand Up @@ -252,7 +255,7 @@ public static <T> Class<T> translatePythonBytecodeToClass(PythonCompiledFunction
null);

translatePythonBytecodeToMethod(methodDescriptor, internalClassName, methodVisitor, pythonCompiledFunction,
isPythonLikeFunction, Integer.MAX_VALUE, isVirtual); // TODO: Use actual python version
isPythonLikeFunction, isVirtual);

classWriter.visitEnd();

Expand Down Expand Up @@ -296,7 +299,7 @@ public static <T> Class<T> translatePythonBytecodeToClass(PythonCompiledFunction
null);

translatePythonBytecodeToMethod(methodDescriptor, internalClassName, methodVisitor, pythonCompiledFunction,
isPythonLikeFunction, Integer.MAX_VALUE, isVirtual); // TODO: Use actual python version
isPythonLikeFunction, isVirtual);

String withoutGenericsSignature = Type.getMethodDescriptor(methodWithoutGenerics);
if (!withoutGenericsSignature.equals(methodDescriptor.getMethodDescriptor())) {
Expand Down Expand Up @@ -991,7 +994,7 @@ public static PythonFunctionType getFunctionType(PythonCompiledFunction pythonCo
}

private static void translatePythonBytecodeToMethod(MethodDescriptor method, String className, MethodVisitor methodVisitor,
PythonCompiledFunction pythonCompiledFunction, boolean isPythonLikeFunction, int pythonVersion, boolean isVirtual) {
PythonCompiledFunction pythonCompiledFunction, boolean isPythonLikeFunction, boolean isVirtual) {
// Apply Method Adapters, which reorder try blocks and check the bytecode to ensure it valid
methodVisitor = MethodVisitorAdapters.adapt(methodVisitor, method);

Expand Down Expand Up @@ -1057,6 +1060,7 @@ private static void translatePythonBytecodeToMethod(MethodDescriptor method, Str

FlowGraph flowGraph = FlowGraph.createFlowGraph(functionMetadata, initialStackMetadata, opcodeList);
List<StackMetadata> stackMetadataForOpcodeIndex = flowGraph.getStackMetadataForOperations();

writeInstructionsForOpcodes(functionMetadata, stackMetadataForOpcodeIndex, opcodeList);

methodVisitor.visitLabel(end);
Expand Down Expand Up @@ -1140,6 +1144,19 @@ public static void writeInstructionsForOpcodes(FunctionMetadata functionMetadata
});
}

var requiredNullVariableSet = new TreeSet<Integer>();
for (Opcode opcode : opcodeList) {
if (opcode instanceof LoadFastAndClearOpcode loadAndClearOpcode) {
requiredNullVariableSet.add(loadAndClearOpcode.getInstruction().arg());
}
}

for (var requiredNullVariable : requiredNullVariableSet) {
methodVisitor.visitInsn(Opcodes.ACONST_NULL);
methodVisitor.visitVarInsn(Opcodes.ASTORE,
stackMetadataForOpcodeIndex.get(0).localVariableHelper.getPythonLocalVariableSlot(requiredNullVariable));
}

for (int i = 0; i < opcodeList.size(); i++) {
StackMetadata stackMetadata = stackMetadataForOpcodeIndex.get(i);
PythonBytecodeInstruction instruction = pythonCompiledFunction.instructionList.get(i);
Expand All @@ -1148,7 +1165,7 @@ public static void writeInstructionsForOpcodes(FunctionMetadata functionMetadata
Label label = exceptionTableTargetLabelMap.get(instruction.offset());
methodVisitor.visitLabel(label);
}
exceptionTableTryBlockMap.getOrDefault(instruction.offset(), List.of()).forEach(Runnable::run);
exceptionTableTryBlockMap.getOrDefault(instruction.offset(), Collections.emptyList()).forEach(Runnable::run);

if (instruction.isJumpTarget() || bytecodeCounterToLabelMap.containsKey(instruction.offset())) {
Label label = bytecodeCounterToLabelMap.computeIfAbsent(instruction.offset(), offset -> new Label());
Expand All @@ -1157,7 +1174,7 @@ public static void writeInstructionsForOpcodes(FunctionMetadata functionMetadata

runAfterLabelAndBeforeArgumentors.accept(instruction);

bytecodeIndexToArgumentorsMap.getOrDefault(instruction.offset(), List.of()).forEach(Runnable::run);
bytecodeIndexToArgumentorsMap.getOrDefault(instruction.offset(), Collections.emptyList()).forEach(Runnable::run);

if (exceptionTableStartLabelMap.containsKey(instruction.offset())) {
Label label = exceptionTableStartLabelMap.get(instruction.offset());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import ai.timefold.jpyinterpreter.opcodes.AbstractOpcode;
import ai.timefold.jpyinterpreter.opcodes.Opcode;
import ai.timefold.jpyinterpreter.opcodes.SelfOpcodeWithoutSource;
import ai.timefold.jpyinterpreter.opcodes.controlflow.ReturnConstantValueOpcode;
import ai.timefold.jpyinterpreter.opcodes.controlflow.ReturnValueOpcode;
import ai.timefold.jpyinterpreter.opcodes.object.DeleteAttrOpcode;
import ai.timefold.jpyinterpreter.opcodes.object.LoadAttrOpcode;
Expand Down Expand Up @@ -788,7 +789,7 @@ private static void createInstanceMethod(PythonLikeType pythonLikeType, ClassWri
function.getReturnType().orElse(BuiltinTypes.BASE_TYPE),
function.totalArgCount() > 0
? function.getParameterTypes().subList(1, function.getParameterTypes().size())
: List.of()));
: Collections.emptyList()));
}

private static void createStaticMethod(PythonLikeType pythonLikeType, ClassWriter classWriter, String internalClassName,
Expand Down Expand Up @@ -1394,6 +1395,9 @@ public static PythonLikeType getPythonReturnTypeOfFunction(PythonCompiledFunctio
flowGraph.visitOperations(ReturnValueOpcode.class, (opcode, stackMetadata) -> {
possibleReturnTypeList.add(stackMetadata.getTOSType());
});
flowGraph.visitOperations(ReturnConstantValueOpcode.class, (opcode, stackMetadata) -> {
possibleReturnTypeList.add(opcode.getConstant(pythonCompiledFunction).$getGenericType());
});

return possibleReturnTypeList.stream()
.reduce(PythonLikeType::unifyWith)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,13 @@ public static PythonFunctionSignature forMethod(Method method) {

public PythonFunctionSignature(MethodDescriptor methodDescriptor,
PythonLikeType returnType, PythonLikeType... parameterTypes) {
this(methodDescriptor, List.of(), extractKeywordArgument(methodDescriptor), returnType, parameterTypes);
this(methodDescriptor, Collections.emptyList(), extractKeywordArgument(methodDescriptor), returnType, parameterTypes);
}

public PythonFunctionSignature(MethodDescriptor methodDescriptor,
PythonLikeType returnType, List<PythonLikeType> parameterTypeList) {
this(methodDescriptor, List.of(), extractKeywordArgument(methodDescriptor), returnType, parameterTypeList);
this(methodDescriptor, Collections.emptyList(), extractKeywordArgument(methodDescriptor), returnType,
parameterTypeList);
}

public PythonFunctionSignature(MethodDescriptor methodDescriptor,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
public final class PythonVersion implements Comparable<PythonVersion> {
private final int hexversion;

public static final PythonVersion PYTHON_3_9 = new PythonVersion(3, 9);
public static final PythonVersion PYTHON_3_10 = new PythonVersion(3, 10);
public static final PythonVersion PYTHON_3_11 = new PythonVersion(3, 11);
public static final PythonVersion PYTHON_3_12 = new PythonVersion(3, 12);

public static final PythonVersion MINIMUM_PYTHON_VERSION = PYTHON_3_9;
public static final PythonVersion MINIMUM_PYTHON_VERSION = PYTHON_3_10;

public PythonVersion(int hexversion) {
this.hexversion = hexversion;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
Expand Down Expand Up @@ -41,7 +42,7 @@ public StackMetadata(LocalVariableHelper localVariableHelper) {
cellVariableValueSources.add(ValueSourceInfo.of(new OpcodeWithoutSource(),
BuiltinTypes.BASE_TYPE));
}
this.callKeywordNameList = List.of();
this.callKeywordNameList = Collections.emptyList();
}

private StackMetadata(LocalVariableHelper localVariableHelper, List<ValueSourceInfo> stackValueSources,
Expand All @@ -62,11 +63,6 @@ public int getStackSize() {
return stackValueSources.size();
}

public List<PythonLikeType> getStackTypeList() {
return stackValueSources.stream().map(ValueSourceInfo::getValueType)
.collect(Collectors.toList());
}

/**
* Returns the list index for the given stack index (stack index is how many
* elements below TOS (i.e. 0 is TOS, 1 is TOS1)).
Expand Down Expand Up @@ -113,7 +109,8 @@ public PythonLikeType getTypeAtStackIndex(int index) {
if (valueSourceInfo != null) {
return valueSourceInfo.valueType;
}
return null;
// Unknown type
return BuiltinTypes.BASE_TYPE;
}

/**
Expand All @@ -126,20 +123,6 @@ public ValueSourceInfo getLocalVariableValueSource(int index) {
return localVariableValueSources.get(index);
}

/**
* Returns the type for the local variable in slot {@code index}
*
* @param index The slot
* @return The type for the local variable in the given slot
*/
public PythonLikeType getLocalVariableType(int index) {
ValueSourceInfo valueSourceInfo = localVariableValueSources.get(index);
if (valueSourceInfo != null) {
return valueSourceInfo.valueType;
}
return null;
}

/**
* Returns the value source for the cell variable in slot {@code index}
*
Expand All @@ -150,20 +133,6 @@ public ValueSourceInfo getCellVariableValueSource(int index) {
return cellVariableValueSources.get(index);
}

/**
* Returns the type for the cell variable in slot {@code index}
*
* @param index The slot
* @return The type for the cell variable in the given slot
*/
public PythonLikeType getCellVariableType(int index) {
ValueSourceInfo valueSourceInfo = cellVariableValueSources.get(index);
if (valueSourceInfo != null) {
return valueSourceInfo.valueType;
}
return null;
}

public PythonLikeType getTOSType() {
return getTypeAtStackIndex(0);
}
Expand Down Expand Up @@ -277,13 +246,6 @@ public StackMetadata pushTemps(PythonLikeType... types) {
return out;
}

public StackMetadata insertTemp(int tosIndex, PythonLikeType type) {
StackMetadata out = copy();
out.stackValueSources.add(stackValueSources.size() - tosIndex,
ValueSourceInfo.of(new OpcodeWithoutSource(), type));
return out;
}

/**
* Return a new StackMetadata with {@code types} as the stack;
* The original stack is cleared.
Expand Down
Loading
Loading