From 632a092cfe9552ca759e99faf6c7e14fbf8e5387 Mon Sep 17 00:00:00 2001 From: Christopher Chianelli Date: Mon, 4 Mar 2024 15:53:41 -0500 Subject: [PATCH] feat: Add expand, concat, consecutive collector, collectAndThen collector (#5) --- ...erators.java => PythonBinaryOperator.java} | 94 +-- .../PythonGeneratorTranslator.java | 4 +- .../jpyinterpreter/PythonLikeObject.java | 4 +- ...rators.java => PythonTernaryOperator.java} | 4 +- .../builtins/BinaryDunderBuiltin.java | 18 +- .../builtins/GlobalBuiltins.java | 4 +- .../builtins/TernaryDunderBuiltin.java | 8 +- .../implementors/CollectionImplementor.java | 8 +- .../DunderOperatorImplementor.java | 30 +- .../implementors/GeneratorImplementor.java | 6 +- .../JavaPythonTypeConversionImplementor.java | 15 +- .../implementors/ObjectImplementor.java | 10 +- .../opcodes/collection/DeleteItemOpcode.java | 4 +- .../opcodes/dunder/BinaryDunderOpcode.java | 6 +- .../opcodes/dunder/DunderOpcodes.java | 58 +- .../jpyinterpreter/types/BuiltinTypes.java | 6 +- .../jpyinterpreter/types/PythonByteArray.java | 32 +- .../jpyinterpreter/types/PythonBytes.java | 18 +- .../types/PythonLikeComparable.java | 10 +- .../jpyinterpreter/types/PythonLikeType.java | 36 +- .../jpyinterpreter/types/PythonNone.java | 6 +- .../jpyinterpreter/types/PythonRange.java | 6 +- .../jpyinterpreter/types/PythonSlice.java | 4 +- .../jpyinterpreter/types/PythonString.java | 22 +- .../types/PythonSuperObject.java | 6 +- .../types/collections/PythonLikeDict.java | 16 +- .../collections/PythonLikeFrozenSet.java | 36 +- .../types/collections/PythonLikeList.java | 26 +- .../types/collections/PythonLikeSet.java | 44 +- .../types/collections/PythonLikeTuple.java | 12 +- .../types/collections/view/DictItemView.java | 20 +- .../types/collections/view/DictKeyView.java | 20 +- .../types/collections/view/DictValueView.java | 4 +- .../types/datetime/PythonDate.java | 8 +- .../types/datetime/PythonDateTime.java | 8 +- .../types/datetime/PythonTimeDelta.java | 22 +- .../types/errors/PythonBaseException.java | 7 +- .../types/numeric/PythonFloat.java | 90 +-- .../types/numeric/PythonInteger.java | 110 ++-- .../types/wrappers/JavaMethodReference.java | 4 + .../types/wrappers/JavaObjectWrapper.java | 329 +++++++---- .../wrappers/WrappingJavaObjectIterator.java | 16 + .../types/wrappers/JavaObjectWrapperTest.java | 136 +++++ .../wrappers/inaccessible/PrivateObject.java | 7 + .../inaccessible/PublicInterface.java | 9 + jpyinterpreter/tox.ini | 3 +- pom.xml | 2 +- tests/test_collectors.py | 123 +++- tests/test_constraint_streams.py | 534 ++++++++++++++++++ tests/test_java_cs_methods.py | 99 ---- .../src/main/python/constraint/__init__.py | 80 +++ .../src/main/python/constraint_stream.py | 450 ++++++++++++++- tox.ini | 3 +- 53 files changed, 1971 insertions(+), 666 deletions(-) rename jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/{PythonBinaryOperators.java => PythonBinaryOperator.java} (63%) rename jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/{PythonTernaryOperators.java => PythonTernaryOperator.java} (88%) create mode 100644 jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/wrappers/WrappingJavaObjectIterator.java create mode 100644 jpyinterpreter/src/test/java/ai/timefold/jpyinterpreter/types/wrappers/JavaObjectWrapperTest.java create mode 100644 jpyinterpreter/src/test/java/ai/timefold/jpyinterpreter/types/wrappers/inaccessible/PrivateObject.java create mode 100644 jpyinterpreter/src/test/java/ai/timefold/jpyinterpreter/types/wrappers/inaccessible/PublicInterface.java create mode 100644 tests/test_constraint_streams.py delete mode 100644 tests/test_java_cs_methods.py diff --git a/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/PythonBinaryOperators.java b/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/PythonBinaryOperator.java similarity index 63% rename from jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/PythonBinaryOperators.java rename to jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/PythonBinaryOperator.java index 4b07f8d7..13a28825 100644 --- a/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/PythonBinaryOperators.java +++ b/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/PythonBinaryOperator.java @@ -9,7 +9,7 @@ * * ex: a.__add__(b) */ -public enum PythonBinaryOperators { +public enum PythonBinaryOperator { // Comparable operations ( from https://docs.python.org/3/reference/datamodel.html#object.__lt__ ) LESS_THAN("<", "__lt__", "__gt__", true), LESS_THAN_OR_EQUAL("<=", "__le__", "__ge__", true), @@ -34,19 +34,19 @@ public enum PythonBinaryOperators { OR("|", "__or__", "__ror__"), // In-place numeric binary operations variations - INPLACE_POWER("**=", "__ipow__", PythonBinaryOperators.POWER), - INPLACE_MULTIPLY("*=", "__imul__", PythonBinaryOperators.MULTIPLY), - INPLACE_MATRIX_MULTIPLY("@=", "__imatmul__", PythonBinaryOperators.MATRIX_MULTIPLY), - INPLACE_FLOOR_DIVIDE("//=", "__ifloordiv__", PythonBinaryOperators.FLOOR_DIVIDE), - INPLACE_TRUE_DIVIDE("/=", "__itruediv__", PythonBinaryOperators.TRUE_DIVIDE), - INPLACE_MODULO("%=", "__imod__", PythonBinaryOperators.MODULO), - INPLACE_ADD("+=", "__iadd__", PythonBinaryOperators.ADD), - INPLACE_SUBTRACT("-=", "__isub__", PythonBinaryOperators.SUBTRACT), - INPLACE_LSHIFT("<<=", "__ilshift__", PythonBinaryOperators.LSHIFT), - INPLACE_RSHIFT(">>=", "__irshift__", PythonBinaryOperators.RSHIFT), - INPLACE_AND("&=", "__iand__", PythonBinaryOperators.AND), - INPLACE_XOR("^=", "__ixor__", PythonBinaryOperators.XOR), - INPLACE_OR("|=", "__ior__", PythonBinaryOperators.OR), + INPLACE_POWER("**=", "__ipow__", PythonBinaryOperator.POWER), + INPLACE_MULTIPLY("*=", "__imul__", PythonBinaryOperator.MULTIPLY), + INPLACE_MATRIX_MULTIPLY("@=", "__imatmul__", PythonBinaryOperator.MATRIX_MULTIPLY), + INPLACE_FLOOR_DIVIDE("//=", "__ifloordiv__", PythonBinaryOperator.FLOOR_DIVIDE), + INPLACE_TRUE_DIVIDE("/=", "__itruediv__", PythonBinaryOperator.TRUE_DIVIDE), + INPLACE_MODULO("%=", "__imod__", PythonBinaryOperator.MODULO), + INPLACE_ADD("+=", "__iadd__", PythonBinaryOperator.ADD), + INPLACE_SUBTRACT("-=", "__isub__", PythonBinaryOperator.SUBTRACT), + INPLACE_LSHIFT("<<=", "__ilshift__", PythonBinaryOperator.LSHIFT), + INPLACE_RSHIFT(">>=", "__irshift__", PythonBinaryOperator.RSHIFT), + INPLACE_AND("&=", "__iand__", PythonBinaryOperator.AND), + INPLACE_XOR("^=", "__ixor__", PythonBinaryOperator.XOR), + INPLACE_OR("|=", "__ior__", PythonBinaryOperator.OR), // List operations // https://docs.python.org/3/reference/datamodel.html#object.__getitem__ @@ -81,9 +81,9 @@ public enum PythonBinaryOperators { public final String dunderMethod; public final String rightDunderMethod; public final boolean isComparisonMethod; - public final PythonBinaryOperators fallbackOperation; + public final PythonBinaryOperator fallbackOperation; - PythonBinaryOperators(String operatorSymbol, String dunderMethod) { + PythonBinaryOperator(String operatorSymbol, String dunderMethod) { this.operatorSymbol = operatorSymbol; this.dunderMethod = dunderMethod; this.rightDunderMethod = null; @@ -91,7 +91,7 @@ public enum PythonBinaryOperators { this.fallbackOperation = null; } - PythonBinaryOperators(String operatorSymbol, String dunderMethod, String rightDunderMethod) { + PythonBinaryOperator(String operatorSymbol, String dunderMethod, String rightDunderMethod) { this.operatorSymbol = operatorSymbol; this.dunderMethod = dunderMethod; this.rightDunderMethod = rightDunderMethod; @@ -99,7 +99,7 @@ public enum PythonBinaryOperators { this.fallbackOperation = null; } - PythonBinaryOperators(String operatorSymbol, String dunderMethod, String rightDunderMethod, boolean isComparisonMethod) { + PythonBinaryOperator(String operatorSymbol, String dunderMethod, String rightDunderMethod, boolean isComparisonMethod) { this.operatorSymbol = operatorSymbol; this.dunderMethod = dunderMethod; this.rightDunderMethod = rightDunderMethod; @@ -107,7 +107,7 @@ public enum PythonBinaryOperators { this.fallbackOperation = null; } - PythonBinaryOperators(String operatorSymbol, String dunderMethod, PythonBinaryOperators fallbackOperation) { + PythonBinaryOperator(String operatorSymbol, String dunderMethod, PythonBinaryOperator fallbackOperation) { this.operatorSymbol = operatorSymbol; this.dunderMethod = dunderMethod; this.rightDunderMethod = null; @@ -135,69 +135,69 @@ public boolean isComparisonMethod() { return isComparisonMethod; } - public Optional getFallbackOperation() { + public Optional getFallbackOperation() { return Optional.ofNullable(fallbackOperation); } - public static PythonBinaryOperators lookup(int instructionArg) { + public static PythonBinaryOperator lookup(int instructionArg) { // As defined by https://github.com/python/cpython/blob/0faa0ba240e815614e5a2900e48007acac41b214/Python/ceval.c#L299 // Binary operations are in alphabetical order (note: CPython refer to Modulo as Remainder), // and are followed by the inplace operations in the same order switch (instructionArg) { case 0: - return PythonBinaryOperators.ADD; + return PythonBinaryOperator.ADD; case 1: - return PythonBinaryOperators.AND; + return PythonBinaryOperator.AND; case 2: - return PythonBinaryOperators.FLOOR_DIVIDE; + return PythonBinaryOperator.FLOOR_DIVIDE; case 3: - return PythonBinaryOperators.LSHIFT; + return PythonBinaryOperator.LSHIFT; case 4: - return PythonBinaryOperators.MATRIX_MULTIPLY; + return PythonBinaryOperator.MATRIX_MULTIPLY; case 5: - return PythonBinaryOperators.MULTIPLY; + return PythonBinaryOperator.MULTIPLY; case 6: - return PythonBinaryOperators.MODULO; + return PythonBinaryOperator.MODULO; case 7: - return PythonBinaryOperators.OR; + return PythonBinaryOperator.OR; case 8: - return PythonBinaryOperators.POWER; + return PythonBinaryOperator.POWER; case 9: - return PythonBinaryOperators.RSHIFT; + return PythonBinaryOperator.RSHIFT; case 10: - return PythonBinaryOperators.SUBTRACT; + return PythonBinaryOperator.SUBTRACT; case 11: - return PythonBinaryOperators.TRUE_DIVIDE; + return PythonBinaryOperator.TRUE_DIVIDE; case 12: - return PythonBinaryOperators.XOR; + return PythonBinaryOperator.XOR; case 13: - return PythonBinaryOperators.INPLACE_ADD; + return PythonBinaryOperator.INPLACE_ADD; case 14: - return PythonBinaryOperators.INPLACE_AND; + return PythonBinaryOperator.INPLACE_AND; case 15: - return PythonBinaryOperators.INPLACE_FLOOR_DIVIDE; + return PythonBinaryOperator.INPLACE_FLOOR_DIVIDE; case 16: - return PythonBinaryOperators.INPLACE_LSHIFT; + return PythonBinaryOperator.INPLACE_LSHIFT; case 17: - return PythonBinaryOperators.INPLACE_MATRIX_MULTIPLY; + return PythonBinaryOperator.INPLACE_MATRIX_MULTIPLY; case 18: - return PythonBinaryOperators.INPLACE_MULTIPLY; + return PythonBinaryOperator.INPLACE_MULTIPLY; case 19: - return PythonBinaryOperators.INPLACE_MODULO; + return PythonBinaryOperator.INPLACE_MODULO; case 20: - return PythonBinaryOperators.INPLACE_OR; + return PythonBinaryOperator.INPLACE_OR; case 21: - return PythonBinaryOperators.INPLACE_POWER; + return PythonBinaryOperator.INPLACE_POWER; case 22: - return PythonBinaryOperators.INPLACE_RSHIFT; + return PythonBinaryOperator.INPLACE_RSHIFT; case 23: - return PythonBinaryOperators.INPLACE_SUBTRACT; + return PythonBinaryOperator.INPLACE_SUBTRACT; case 24: - return PythonBinaryOperators.INPLACE_TRUE_DIVIDE; + return PythonBinaryOperator.INPLACE_TRUE_DIVIDE; case 25: - return PythonBinaryOperators.INPLACE_XOR; + return PythonBinaryOperator.INPLACE_XOR; default: throw new IllegalArgumentException("Unknown binary op id: " + instructionArg); diff --git a/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/PythonGeneratorTranslator.java b/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/PythonGeneratorTranslator.java index ca9f1836..a5bb14d7 100644 --- a/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/PythonGeneratorTranslator.java +++ b/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/PythonGeneratorTranslator.java @@ -633,7 +633,7 @@ private static void generateAdvanceGeneratorMethodForYieldFrom(ClassWriter class "sentValue", Type.getDescriptor(PythonLikeObject.class)); FunctionImplementor.callBinaryMethod(methodVisitor, - PythonBinaryOperators.SEND.dunderMethod); + PythonBinaryOperator.SEND.dunderMethod); break; } case 2: { // throw @@ -687,7 +687,7 @@ private static void generateAdvanceGeneratorMethodForYieldFrom(ClassWriter class // Swap so it Generator, Throwable instead of Throwable, Generator methodVisitor.visitInsn(Opcodes.SWAP); FunctionImplementor.callBinaryMethod(methodVisitor, - PythonBinaryOperators.THROW.dunderMethod); + PythonBinaryOperator.THROW.dunderMethod); break; } } diff --git a/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/PythonLikeObject.java b/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/PythonLikeObject.java index b2d65039..bfafca41 100644 --- a/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/PythonLikeObject.java +++ b/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/PythonLikeObject.java @@ -88,9 +88,9 @@ default PythonLikeType __getGenericType() { PythonLikeType type = __getType(); PythonLikeObject typeResult = type.__getAttributeOrNull(name); if (typeResult != null) { - PythonLikeObject maybeDescriptor = typeResult.__getAttributeOrNull(PythonTernaryOperators.GET.dunderMethod); + PythonLikeObject maybeDescriptor = typeResult.__getAttributeOrNull(PythonTernaryOperator.GET.dunderMethod); if (maybeDescriptor == null) { - maybeDescriptor = typeResult.__getType().__getAttributeOrNull(PythonTernaryOperators.GET.dunderMethod); + maybeDescriptor = typeResult.__getType().__getAttributeOrNull(PythonTernaryOperator.GET.dunderMethod); } if (maybeDescriptor != null) { diff --git a/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/PythonTernaryOperators.java b/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/PythonTernaryOperator.java similarity index 88% rename from jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/PythonTernaryOperators.java rename to jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/PythonTernaryOperator.java index bab7c95f..604e5085 100644 --- a/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/PythonTernaryOperators.java +++ b/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/PythonTernaryOperator.java @@ -6,7 +6,7 @@ * * ex: a.__setitem__(key, value) */ -public enum PythonTernaryOperators { +public enum PythonTernaryOperator { // Descriptor operations // https://docs.python.org/3/howto/descriptor.html GET("__get__"), @@ -21,7 +21,7 @@ public enum PythonTernaryOperators { public final String dunderMethod; - PythonTernaryOperators(String dunderMethod) { + PythonTernaryOperator(String dunderMethod) { this.dunderMethod = dunderMethod; } diff --git a/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/builtins/BinaryDunderBuiltin.java b/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/builtins/BinaryDunderBuiltin.java index b5c1bb0a..82dab776 100644 --- a/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/builtins/BinaryDunderBuiltin.java +++ b/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/builtins/BinaryDunderBuiltin.java @@ -3,7 +3,7 @@ import java.util.List; import java.util.Map; -import ai.timefold.jpyinterpreter.PythonBinaryOperators; +import ai.timefold.jpyinterpreter.PythonBinaryOperator; import ai.timefold.jpyinterpreter.PythonLikeObject; import ai.timefold.jpyinterpreter.types.PythonLikeFunction; import ai.timefold.jpyinterpreter.types.PythonString; @@ -12,19 +12,19 @@ public class BinaryDunderBuiltin implements PythonLikeFunction { private final String DUNDER_METHOD_NAME; - public static final BinaryDunderBuiltin DIVMOD = new BinaryDunderBuiltin(PythonBinaryOperators.DIVMOD); - public static final BinaryDunderBuiltin ADD = new BinaryDunderBuiltin(PythonBinaryOperators.ADD); - public static final BinaryDunderBuiltin LESS_THAN = new BinaryDunderBuiltin(PythonBinaryOperators.LESS_THAN); - public static final BinaryDunderBuiltin GET_ITEM = new BinaryDunderBuiltin(PythonBinaryOperators.GET_ITEM); - public static final BinaryDunderBuiltin GET_ATTRIBUTE = new BinaryDunderBuiltin(PythonBinaryOperators.GET_ATTRIBUTE); - public static final BinaryDunderBuiltin POWER = new BinaryDunderBuiltin(PythonBinaryOperators.POWER); - public static final BinaryDunderBuiltin FORMAT = new BinaryDunderBuiltin(PythonBinaryOperators.FORMAT); + public static final BinaryDunderBuiltin DIVMOD = new BinaryDunderBuiltin(PythonBinaryOperator.DIVMOD); + public static final BinaryDunderBuiltin ADD = new BinaryDunderBuiltin(PythonBinaryOperator.ADD); + public static final BinaryDunderBuiltin LESS_THAN = new BinaryDunderBuiltin(PythonBinaryOperator.LESS_THAN); + public static final BinaryDunderBuiltin GET_ITEM = new BinaryDunderBuiltin(PythonBinaryOperator.GET_ITEM); + public static final BinaryDunderBuiltin GET_ATTRIBUTE = new BinaryDunderBuiltin(PythonBinaryOperator.GET_ATTRIBUTE); + public static final BinaryDunderBuiltin POWER = new BinaryDunderBuiltin(PythonBinaryOperator.POWER); + public static final BinaryDunderBuiltin FORMAT = new BinaryDunderBuiltin(PythonBinaryOperator.FORMAT); public BinaryDunderBuiltin(String dunderMethodName) { DUNDER_METHOD_NAME = dunderMethodName; } - public BinaryDunderBuiltin(PythonBinaryOperators operator) { + public BinaryDunderBuiltin(PythonBinaryOperator operator) { DUNDER_METHOD_NAME = operator.getDunderMethod(); } diff --git a/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/builtins/GlobalBuiltins.java b/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/builtins/GlobalBuiltins.java index 1b45daed..7681afef 100644 --- a/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/builtins/GlobalBuiltins.java +++ b/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/builtins/GlobalBuiltins.java @@ -37,7 +37,7 @@ import java.util.stream.IntStream; import java.util.stream.StreamSupport; -import ai.timefold.jpyinterpreter.PythonBinaryOperators; +import ai.timefold.jpyinterpreter.PythonBinaryOperator; import ai.timefold.jpyinterpreter.PythonBytecodeToJavaBytecodeTranslator; import ai.timefold.jpyinterpreter.PythonInterpreter; import ai.timefold.jpyinterpreter.PythonLikeObject; @@ -1377,7 +1377,7 @@ public static PythonLikeObject reversed(List positionalArgs, } if (sequenceType.__getAttributeOrNull(PythonUnaryOperator.LENGTH.getDunderMethod()) != null && - sequenceType.__getAttributeOrNull(PythonBinaryOperators.GET_ITEM.getDunderMethod()) != null) { + sequenceType.__getAttributeOrNull(PythonBinaryOperator.GET_ITEM.getDunderMethod()) != null) { PythonInteger length = (PythonInteger) UnaryDunderBuiltin.LENGTH.invoke(sequence); Iterator reversedIterator = new Iterator<>() { PythonInteger current = length.subtract(PythonInteger.ONE); diff --git a/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/builtins/TernaryDunderBuiltin.java b/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/builtins/TernaryDunderBuiltin.java index b13b94bc..78cd77b4 100644 --- a/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/builtins/TernaryDunderBuiltin.java +++ b/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/builtins/TernaryDunderBuiltin.java @@ -4,7 +4,7 @@ import java.util.Map; import ai.timefold.jpyinterpreter.PythonLikeObject; -import ai.timefold.jpyinterpreter.PythonTernaryOperators; +import ai.timefold.jpyinterpreter.PythonTernaryOperator; import ai.timefold.jpyinterpreter.types.PythonLikeFunction; import ai.timefold.jpyinterpreter.types.PythonString; import ai.timefold.jpyinterpreter.types.errors.ValueError; @@ -13,14 +13,14 @@ public class TernaryDunderBuiltin implements PythonLikeFunction { private final String DUNDER_METHOD_NAME; public static final TernaryDunderBuiltin POWER = new TernaryDunderBuiltin("__pow__"); - public static final TernaryDunderBuiltin SETATTR = new TernaryDunderBuiltin(PythonTernaryOperators.SET_ATTRIBUTE); - public static final TernaryDunderBuiltin GET_DESCRIPTOR = new TernaryDunderBuiltin(PythonTernaryOperators.GET); + public static final TernaryDunderBuiltin SETATTR = new TernaryDunderBuiltin(PythonTernaryOperator.SET_ATTRIBUTE); + public static final TernaryDunderBuiltin GET_DESCRIPTOR = new TernaryDunderBuiltin(PythonTernaryOperator.GET); public TernaryDunderBuiltin(String dunderMethodName) { DUNDER_METHOD_NAME = dunderMethodName; } - public TernaryDunderBuiltin(PythonTernaryOperators operator) { + public TernaryDunderBuiltin(PythonTernaryOperator operator) { DUNDER_METHOD_NAME = operator.getDunderMethod(); } diff --git a/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/implementors/CollectionImplementor.java b/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/implementors/CollectionImplementor.java index a8bd1c97..6b42d0c2 100644 --- a/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/implementors/CollectionImplementor.java +++ b/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/implementors/CollectionImplementor.java @@ -9,10 +9,10 @@ import ai.timefold.jpyinterpreter.FunctionMetadata; import ai.timefold.jpyinterpreter.LocalVariableHelper; -import ai.timefold.jpyinterpreter.PythonBinaryOperators; +import ai.timefold.jpyinterpreter.PythonBinaryOperator; import ai.timefold.jpyinterpreter.PythonBytecodeInstruction; import ai.timefold.jpyinterpreter.PythonLikeObject; -import ai.timefold.jpyinterpreter.PythonTernaryOperators; +import ai.timefold.jpyinterpreter.PythonTernaryOperator; import ai.timefold.jpyinterpreter.PythonUnaryOperator; import ai.timefold.jpyinterpreter.StackMetadata; import ai.timefold.jpyinterpreter.types.PythonSlice; @@ -481,7 +481,7 @@ public static void containsOperator(MethodVisitor methodVisitor, StackMetadata s DunderOperatorImplementor.binaryOperator(methodVisitor, stackMetadata .pop(2) .push(stackMetadata.getTOSValueSource()) - .push(stackMetadata.getValueSourceForStackIndex(1)), PythonBinaryOperators.CONTAINS); + .push(stackMetadata.getValueSourceForStackIndex(1)), PythonBinaryOperator.CONTAINS); // TODO: implement fallback on __iter__ if __contains__ does not exist if (instruction.arg == 1) { PythonBuiltinOperatorImplementor.performNotOnTOS(methodVisitor); @@ -501,7 +501,7 @@ public static void setItem(FunctionMetadata functionMetadata, StackMetadata stac DunderOperatorImplementor.ternaryOperator(functionMetadata, stackMetadata.pop(3) .push(stackMetadata.getValueSourceForStackIndex(1)) .push(stackMetadata.getValueSourceForStackIndex(0)) - .push(stackMetadata.getValueSourceForStackIndex(2)), PythonTernaryOperators.SET_ITEM); + .push(stackMetadata.getValueSourceForStackIndex(2)), PythonTernaryOperator.SET_ITEM); StackManipulationImplementor.popTOS(methodVisitor); } diff --git a/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/implementors/DunderOperatorImplementor.java b/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/implementors/DunderOperatorImplementor.java index ddc4d816..8fe070a8 100644 --- a/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/implementors/DunderOperatorImplementor.java +++ b/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/implementors/DunderOperatorImplementor.java @@ -9,10 +9,10 @@ import ai.timefold.jpyinterpreter.FunctionMetadata; import ai.timefold.jpyinterpreter.LocalVariableHelper; import ai.timefold.jpyinterpreter.MethodDescriptor; -import ai.timefold.jpyinterpreter.PythonBinaryOperators; +import ai.timefold.jpyinterpreter.PythonBinaryOperator; import ai.timefold.jpyinterpreter.PythonFunctionSignature; import ai.timefold.jpyinterpreter.PythonLikeObject; -import ai.timefold.jpyinterpreter.PythonTernaryOperators; +import ai.timefold.jpyinterpreter.PythonTernaryOperator; import ai.timefold.jpyinterpreter.PythonUnaryOperator; import ai.timefold.jpyinterpreter.StackMetadata; import ai.timefold.jpyinterpreter.types.BuiltinTypes; @@ -109,19 +109,19 @@ public static void unaryOperator(MethodVisitor methodVisitor, PythonUnaryOperato } public static void binaryOperator(MethodVisitor methodVisitor, StackMetadata stackMetadata, - PythonBinaryOperators operator) { + PythonBinaryOperator operator) { binaryOperator(methodVisitor, stackMetadata, operator, true, true, false); } private static void binaryOperator(MethodVisitor methodVisitor, StackMetadata stackMetadata, - PythonBinaryOperators operator, boolean isLeft, boolean leftCheckSuccessful, + PythonBinaryOperator operator, boolean isLeft, boolean leftCheckSuccessful, boolean forceFallback) { PythonLikeType leftOperand = Optional.ofNullable(stackMetadata.getTypeAtStackIndex(1)).orElse(BuiltinTypes.BASE_TYPE); PythonLikeType rightOperand = Optional.ofNullable(stackMetadata.getTypeAtStackIndex(0)).orElse(BuiltinTypes.BASE_TYPE); - PythonBinaryOperators actualOperator = operator; + PythonBinaryOperator actualOperator = operator; if (forceFallback || (!isLeft && operator.getFallbackOperation().isPresent())) { actualOperator = operator.getFallbackOperation().get(); } @@ -236,7 +236,7 @@ private static void binaryOperator(MethodVisitor methodVisitor, StackMetadata st } private static void raiseUnsupportedType(MethodVisitor methodVisitor, LocalVariableHelper localVariableHelper, - PythonBinaryOperators operator) { + PythonBinaryOperator operator) { int right = localVariableHelper.newLocal(); int left = localVariableHelper.newLocal(); @@ -331,7 +331,7 @@ private static void raiseUnsupportedType(MethodVisitor methodVisitor, LocalVaria * */ public static void binaryOperator(MethodVisitor methodVisitor, LocalVariableHelper localVariableHelper, - PythonBinaryOperators operator) { + PythonBinaryOperator operator) { Label noLeftMethod = new Label(); methodVisitor.visitInsn(Opcodes.DUP2); if (operator.hasRightDunderMethod() || operator.getFallbackOperation().isPresent()) { @@ -472,7 +472,7 @@ public static void binaryOperator(MethodVisitor methodVisitor, LocalVariableHelp } public static void binaryOperatorOnlyRight(MethodVisitor methodVisitor, LocalVariableHelper localVariableHelper, - PythonBinaryOperators operator) { + PythonBinaryOperator operator) { Label done = new Label(); Label raiseError = new Label(); Label noRightMethod = new Label(); @@ -560,7 +560,7 @@ public static void binaryOperatorOnlyRight(MethodVisitor methodVisitor, LocalVar * */ public static void ternaryOperator(FunctionMetadata functionMetadata, StackMetadata stackMetadata, - PythonTernaryOperators operator) { + PythonTernaryOperator operator) { MethodVisitor methodVisitor = functionMetadata.methodVisitor; StackManipulationImplementor.rotateThree(methodVisitor); @@ -626,20 +626,20 @@ private static void pushArgumentIntoList(MethodVisitor methodVisitor) { public static void compareValues(MethodVisitor methodVisitor, StackMetadata stackMetadata, CompareOp op) { switch (op) { case LESS_THAN: - binaryOperator(methodVisitor, stackMetadata, PythonBinaryOperators.LESS_THAN); + binaryOperator(methodVisitor, stackMetadata, PythonBinaryOperator.LESS_THAN); break; case LESS_THAN_OR_EQUALS: - binaryOperator(methodVisitor, stackMetadata, PythonBinaryOperators.LESS_THAN_OR_EQUAL); + binaryOperator(methodVisitor, stackMetadata, PythonBinaryOperator.LESS_THAN_OR_EQUAL); break; case EQUALS: case NOT_EQUALS: binaryOpOverridingLeftIfSpecific(methodVisitor, stackMetadata, op); break; case GREATER_THAN: - binaryOperator(methodVisitor, stackMetadata, PythonBinaryOperators.GREATER_THAN); + binaryOperator(methodVisitor, stackMetadata, PythonBinaryOperator.GREATER_THAN); break; case GREATER_THAN_OR_EQUALS: - binaryOperator(methodVisitor, stackMetadata, PythonBinaryOperators.GREATER_THAN_OR_EQUAL); + binaryOperator(methodVisitor, stackMetadata, PythonBinaryOperator.GREATER_THAN_OR_EQUAL); break; default: throw new IllegalStateException("Unhandled branch: " + op); @@ -656,8 +656,8 @@ private static void binaryOpOverridingLeftIfSpecific(MethodVisitor methodVisitor throw new IllegalArgumentException("Should only be called for equals and not equals"); } - PythonBinaryOperators operator = - (op == CompareOp.EQUALS) ? PythonBinaryOperators.EQUAL : PythonBinaryOperators.NOT_EQUAL; + PythonBinaryOperator operator = + (op == CompareOp.EQUALS) ? PythonBinaryOperator.EQUAL : PythonBinaryOperator.NOT_EQUAL; // If we know TOS1 defines == or !=, we don't need to go here if (stackMetadata.getTypeAtStackIndex(1).getDefiningTypeOrNull(operator.getDunderMethod()) != BuiltinTypes.BASE_TYPE) { diff --git a/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/implementors/GeneratorImplementor.java b/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/implementors/GeneratorImplementor.java index 684f2cd9..8459a21e 100644 --- a/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/implementors/GeneratorImplementor.java +++ b/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/implementors/GeneratorImplementor.java @@ -5,7 +5,7 @@ import ai.timefold.jpyinterpreter.BytecodeSwitchImplementor; import ai.timefold.jpyinterpreter.FunctionMetadata; -import ai.timefold.jpyinterpreter.PythonBinaryOperators; +import ai.timefold.jpyinterpreter.PythonBinaryOperator; import ai.timefold.jpyinterpreter.PythonBytecodeInstruction; import ai.timefold.jpyinterpreter.PythonGeneratorTranslator; import ai.timefold.jpyinterpreter.PythonLikeObject; @@ -235,7 +235,7 @@ public static void progressSubgenerator(FunctionMetadata functionMetadata, Stack "sentValue", Type.getDescriptor(PythonLikeObject.class)); FunctionImplementor.callBinaryMethod(methodVisitor, - PythonBinaryOperators.SEND.dunderMethod); + PythonBinaryOperator.SEND.dunderMethod); break; } case 2: { // throw @@ -287,7 +287,7 @@ public static void progressSubgenerator(FunctionMetadata functionMetadata, Stack // Swap so it Generator, Throwable instead of Throwable, Generator methodVisitor.visitInsn(Opcodes.SWAP); FunctionImplementor.callBinaryMethod(methodVisitor, - PythonBinaryOperators.THROW.dunderMethod); + PythonBinaryOperator.THROW.dunderMethod); break; } } diff --git a/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/implementors/JavaPythonTypeConversionImplementor.java b/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/implementors/JavaPythonTypeConversionImplementor.java index 01c5fe8a..5fdb52c2 100644 --- a/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/implementors/JavaPythonTypeConversionImplementor.java +++ b/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/implementors/JavaPythonTypeConversionImplementor.java @@ -2,6 +2,7 @@ import java.lang.reflect.Field; import java.math.BigInteger; +import java.util.IdentityHashMap; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -49,10 +50,19 @@ public class JavaPythonTypeConversionImplementor { * Wraps {@code object} to a PythonLikeObject. */ public static PythonLikeObject wrapJavaObject(Object object) { + return wrapJavaObject(object, new IdentityHashMap<>()); + } + + public static PythonLikeObject wrapJavaObject(Object object, Map createdObjectMap) { if (object == null) { return PythonNone.INSTANCE; } + var existingObject = createdObjectMap.get(object); + if (existingObject != null) { + return existingObject; + } + if (object instanceof OpaqueJavaReference) { return ((OpaqueJavaReference) object).proxy(); } @@ -88,6 +98,7 @@ public static PythonLikeObject wrapJavaObject(Object object) { if (object instanceof List) { PythonLikeList out = new PythonLikeList(); + createdObjectMap.put(object, out); for (Object item : (List) object) { out.add(wrapJavaObject(item)); } @@ -96,6 +107,7 @@ public static PythonLikeObject wrapJavaObject(Object object) { if (object instanceof Set) { PythonLikeSet out = new PythonLikeSet(); + createdObjectMap.put(object, out); for (Object item : (Set) object) { out.add(wrapJavaObject(item)); } @@ -104,6 +116,7 @@ public static PythonLikeObject wrapJavaObject(Object object) { if (object instanceof Map) { PythonLikeDict out = new PythonLikeDict(); + createdObjectMap.put(object, out); Set> entrySet = ((Map) object).entrySet(); for (Map.Entry entry : entrySet) { out.put(wrapJavaObject(entry.getKey()), wrapJavaObject(entry.getValue())); @@ -123,7 +136,7 @@ public static PythonLikeObject wrapJavaObject(Object object) { } // Default: return a JavaObjectWrapper - return new JavaObjectWrapper(object); + return new JavaObjectWrapper(object, createdObjectMap); } /** diff --git a/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/implementors/ObjectImplementor.java b/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/implementors/ObjectImplementor.java index a9ecaee3..238aa31f 100644 --- a/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/implementors/ObjectImplementor.java +++ b/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/implementors/ObjectImplementor.java @@ -5,10 +5,10 @@ import ai.timefold.jpyinterpreter.FieldDescriptor; import ai.timefold.jpyinterpreter.FunctionMetadata; import ai.timefold.jpyinterpreter.LocalVariableHelper; -import ai.timefold.jpyinterpreter.PythonBinaryOperators; +import ai.timefold.jpyinterpreter.PythonBinaryOperator; import ai.timefold.jpyinterpreter.PythonBytecodeInstruction; import ai.timefold.jpyinterpreter.PythonLikeObject; -import ai.timefold.jpyinterpreter.PythonTernaryOperators; +import ai.timefold.jpyinterpreter.PythonTernaryOperator; import ai.timefold.jpyinterpreter.StackMetadata; import ai.timefold.jpyinterpreter.types.BuiltinTypes; import ai.timefold.jpyinterpreter.types.PythonLikeType; @@ -88,7 +88,7 @@ public static void getAttribute(FunctionMetadata functionMetadata, MethodVisitor PythonConstantsImplementor.loadName(methodVisitor, className, instruction.arg); DunderOperatorImplementor.binaryOperator(methodVisitor, stackMetadata.pushTemp(BuiltinTypes.STRING_TYPE), - PythonBinaryOperators.GET_ATTRIBUTE); + PythonBinaryOperator.GET_ATTRIBUTE); } } @@ -112,7 +112,7 @@ public static void deleteAttribute(FunctionMetadata functionMetadata, MethodVisi PythonConstantsImplementor.loadName(methodVisitor, className, instruction.arg); DunderOperatorImplementor.binaryOperator(methodVisitor, stackMetadata.pushTemp(BuiltinTypes.STRING_TYPE), - PythonBinaryOperators.DELETE_ATTRIBUTE); + PythonBinaryOperator.DELETE_ATTRIBUTE); } } @@ -141,7 +141,7 @@ public static void setAttribute(FunctionMetadata functionMetadata, MethodVisitor .push(stackMetadata.getValueSourceForStackIndex(0)) .pushTemp(BuiltinTypes.STRING_TYPE) .push(stackMetadata.getValueSourceForStackIndex(1)), - PythonTernaryOperators.SET_ATTRIBUTE); + PythonTernaryOperator.SET_ATTRIBUTE); } } } diff --git a/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/opcodes/collection/DeleteItemOpcode.java b/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/opcodes/collection/DeleteItemOpcode.java index 0a47f2f7..ee0dad2d 100644 --- a/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/opcodes/collection/DeleteItemOpcode.java +++ b/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/opcodes/collection/DeleteItemOpcode.java @@ -1,7 +1,7 @@ package ai.timefold.jpyinterpreter.opcodes.collection; import ai.timefold.jpyinterpreter.FunctionMetadata; -import ai.timefold.jpyinterpreter.PythonBinaryOperators; +import ai.timefold.jpyinterpreter.PythonBinaryOperator; import ai.timefold.jpyinterpreter.PythonBytecodeInstruction; import ai.timefold.jpyinterpreter.StackMetadata; import ai.timefold.jpyinterpreter.implementors.DunderOperatorImplementor; @@ -23,7 +23,7 @@ protected StackMetadata getStackMetadataAfterInstruction(FunctionMetadata functi @Override public void implement(FunctionMetadata functionMetadata, StackMetadata stackMetadata) { DunderOperatorImplementor.binaryOperator(functionMetadata.methodVisitor, stackMetadata, - PythonBinaryOperators.DELETE_ITEM); + PythonBinaryOperator.DELETE_ITEM); functionMetadata.methodVisitor.visitInsn(Opcodes.POP); // DELETE_ITEM ignore results of delete function } } diff --git a/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/opcodes/dunder/BinaryDunderOpcode.java b/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/opcodes/dunder/BinaryDunderOpcode.java index f036b169..0acc766e 100644 --- a/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/opcodes/dunder/BinaryDunderOpcode.java +++ b/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/opcodes/dunder/BinaryDunderOpcode.java @@ -3,7 +3,7 @@ import java.util.Optional; import ai.timefold.jpyinterpreter.FunctionMetadata; -import ai.timefold.jpyinterpreter.PythonBinaryOperators; +import ai.timefold.jpyinterpreter.PythonBinaryOperator; import ai.timefold.jpyinterpreter.PythonBytecodeInstruction; import ai.timefold.jpyinterpreter.PythonFunctionSignature; import ai.timefold.jpyinterpreter.StackMetadata; @@ -16,9 +16,9 @@ public class BinaryDunderOpcode extends AbstractOpcode { - final PythonBinaryOperators operator; + final PythonBinaryOperator operator; - public BinaryDunderOpcode(PythonBytecodeInstruction instruction, PythonBinaryOperators operator) { + public BinaryDunderOpcode(PythonBytecodeInstruction instruction, PythonBinaryOperator operator) { super(instruction); this.operator = operator; } diff --git a/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/opcodes/dunder/DunderOpcodes.java b/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/opcodes/dunder/DunderOpcodes.java index 9f4f8cbc..170fea3a 100644 --- a/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/opcodes/dunder/DunderOpcodes.java +++ b/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/opcodes/dunder/DunderOpcodes.java @@ -2,7 +2,7 @@ import java.util.Optional; -import ai.timefold.jpyinterpreter.PythonBinaryOperators; +import ai.timefold.jpyinterpreter.PythonBinaryOperator; import ai.timefold.jpyinterpreter.PythonBytecodeInstruction; import ai.timefold.jpyinterpreter.PythonUnaryOperator; import ai.timefold.jpyinterpreter.PythonVersion; @@ -30,92 +30,92 @@ public static Optional lookupOpcodeForInstruction(PythonBytecodeInstruct } case BINARY_OP: { - return Optional.of(new BinaryDunderOpcode(instruction, PythonBinaryOperators.lookup(instruction.arg))); + return Optional.of(new BinaryDunderOpcode(instruction, PythonBinaryOperator.lookup(instruction.arg))); } case BINARY_POWER: { - return Optional.of(new BinaryDunderOpcode(instruction, PythonBinaryOperators.POWER)); + return Optional.of(new BinaryDunderOpcode(instruction, PythonBinaryOperator.POWER)); } case BINARY_MULTIPLY: { - return Optional.of(new BinaryDunderOpcode(instruction, PythonBinaryOperators.MULTIPLY)); + return Optional.of(new BinaryDunderOpcode(instruction, PythonBinaryOperator.MULTIPLY)); } case BINARY_MATRIX_MULTIPLY: { - return Optional.of(new BinaryDunderOpcode(instruction, PythonBinaryOperators.MATRIX_MULTIPLY)); + return Optional.of(new BinaryDunderOpcode(instruction, PythonBinaryOperator.MATRIX_MULTIPLY)); } case BINARY_FLOOR_DIVIDE: { - return Optional.of(new BinaryDunderOpcode(instruction, PythonBinaryOperators.FLOOR_DIVIDE)); + return Optional.of(new BinaryDunderOpcode(instruction, PythonBinaryOperator.FLOOR_DIVIDE)); } case BINARY_TRUE_DIVIDE: { - return Optional.of(new BinaryDunderOpcode(instruction, PythonBinaryOperators.TRUE_DIVIDE)); + return Optional.of(new BinaryDunderOpcode(instruction, PythonBinaryOperator.TRUE_DIVIDE)); } case BINARY_MODULO: { - return Optional.of(new BinaryDunderOpcode(instruction, PythonBinaryOperators.MODULO)); + return Optional.of(new BinaryDunderOpcode(instruction, PythonBinaryOperator.MODULO)); } case BINARY_ADD: { - return Optional.of(new BinaryDunderOpcode(instruction, PythonBinaryOperators.ADD)); + return Optional.of(new BinaryDunderOpcode(instruction, PythonBinaryOperator.ADD)); } case BINARY_SUBTRACT: { - return Optional.of(new BinaryDunderOpcode(instruction, PythonBinaryOperators.SUBTRACT)); + return Optional.of(new BinaryDunderOpcode(instruction, PythonBinaryOperator.SUBTRACT)); } case BINARY_SUBSCR: { - return Optional.of(new BinaryDunderOpcode(instruction, PythonBinaryOperators.GET_ITEM)); + return Optional.of(new BinaryDunderOpcode(instruction, PythonBinaryOperator.GET_ITEM)); } case BINARY_LSHIFT: { - return Optional.of(new BinaryDunderOpcode(instruction, PythonBinaryOperators.LSHIFT)); + return Optional.of(new BinaryDunderOpcode(instruction, PythonBinaryOperator.LSHIFT)); } case BINARY_RSHIFT: { - return Optional.of(new BinaryDunderOpcode(instruction, PythonBinaryOperators.RSHIFT)); + return Optional.of(new BinaryDunderOpcode(instruction, PythonBinaryOperator.RSHIFT)); } case BINARY_AND: { - return Optional.of(new BinaryDunderOpcode(instruction, PythonBinaryOperators.AND)); + return Optional.of(new BinaryDunderOpcode(instruction, PythonBinaryOperator.AND)); } case BINARY_XOR: { - return Optional.of(new BinaryDunderOpcode(instruction, PythonBinaryOperators.XOR)); + return Optional.of(new BinaryDunderOpcode(instruction, PythonBinaryOperator.XOR)); } case BINARY_OR: { - return Optional.of(new BinaryDunderOpcode(instruction, PythonBinaryOperators.OR)); + return Optional.of(new BinaryDunderOpcode(instruction, PythonBinaryOperator.OR)); } // ************************************************** // In-place Dunder Operations // ************************************************** case INPLACE_POWER: { - return Optional.of(new BinaryDunderOpcode(instruction, PythonBinaryOperators.INPLACE_POWER)); + return Optional.of(new BinaryDunderOpcode(instruction, PythonBinaryOperator.INPLACE_POWER)); } case INPLACE_MULTIPLY: { - return Optional.of(new BinaryDunderOpcode(instruction, PythonBinaryOperators.INPLACE_MULTIPLY)); + return Optional.of(new BinaryDunderOpcode(instruction, PythonBinaryOperator.INPLACE_MULTIPLY)); } case INPLACE_MATRIX_MULTIPLY: { - return Optional.of(new BinaryDunderOpcode(instruction, PythonBinaryOperators.INPLACE_MATRIX_MULTIPLY)); + return Optional.of(new BinaryDunderOpcode(instruction, PythonBinaryOperator.INPLACE_MATRIX_MULTIPLY)); } case INPLACE_FLOOR_DIVIDE: { - return Optional.of(new BinaryDunderOpcode(instruction, PythonBinaryOperators.INPLACE_FLOOR_DIVIDE)); + return Optional.of(new BinaryDunderOpcode(instruction, PythonBinaryOperator.INPLACE_FLOOR_DIVIDE)); } case INPLACE_TRUE_DIVIDE: { - return Optional.of(new BinaryDunderOpcode(instruction, PythonBinaryOperators.INPLACE_TRUE_DIVIDE)); + return Optional.of(new BinaryDunderOpcode(instruction, PythonBinaryOperator.INPLACE_TRUE_DIVIDE)); } case INPLACE_MODULO: { - return Optional.of(new BinaryDunderOpcode(instruction, PythonBinaryOperators.INPLACE_MODULO)); + return Optional.of(new BinaryDunderOpcode(instruction, PythonBinaryOperator.INPLACE_MODULO)); } case INPLACE_ADD: { - return Optional.of(new BinaryDunderOpcode(instruction, PythonBinaryOperators.INPLACE_ADD)); + return Optional.of(new BinaryDunderOpcode(instruction, PythonBinaryOperator.INPLACE_ADD)); } case INPLACE_SUBTRACT: { - return Optional.of(new BinaryDunderOpcode(instruction, PythonBinaryOperators.INPLACE_SUBTRACT)); + return Optional.of(new BinaryDunderOpcode(instruction, PythonBinaryOperator.INPLACE_SUBTRACT)); } case INPLACE_LSHIFT: { - return Optional.of(new BinaryDunderOpcode(instruction, PythonBinaryOperators.INPLACE_LSHIFT)); + return Optional.of(new BinaryDunderOpcode(instruction, PythonBinaryOperator.INPLACE_LSHIFT)); } case INPLACE_RSHIFT: { - return Optional.of(new BinaryDunderOpcode(instruction, PythonBinaryOperators.INPLACE_RSHIFT)); + return Optional.of(new BinaryDunderOpcode(instruction, PythonBinaryOperator.INPLACE_RSHIFT)); } case INPLACE_AND: { - return Optional.of(new BinaryDunderOpcode(instruction, PythonBinaryOperators.INPLACE_AND)); + return Optional.of(new BinaryDunderOpcode(instruction, PythonBinaryOperator.INPLACE_AND)); } case INPLACE_XOR: { - return Optional.of(new BinaryDunderOpcode(instruction, PythonBinaryOperators.INPLACE_XOR)); + return Optional.of(new BinaryDunderOpcode(instruction, PythonBinaryOperator.INPLACE_XOR)); } case INPLACE_OR: { - return Optional.of(new BinaryDunderOpcode(instruction, PythonBinaryOperators.INPLACE_OR)); + return Optional.of(new BinaryDunderOpcode(instruction, PythonBinaryOperator.INPLACE_OR)); } default: diff --git a/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/BuiltinTypes.java b/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/BuiltinTypes.java index 63e845cf..b2f323f7 100644 --- a/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/BuiltinTypes.java +++ b/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/BuiltinTypes.java @@ -9,7 +9,7 @@ import ai.timefold.jpyinterpreter.PythonBytecodeToJavaBytecodeTranslator; import ai.timefold.jpyinterpreter.PythonLikeObject; import ai.timefold.jpyinterpreter.PythonOverloadImplementor; -import ai.timefold.jpyinterpreter.PythonTernaryOperators; +import ai.timefold.jpyinterpreter.PythonTernaryOperator; import ai.timefold.jpyinterpreter.builtins.FunctionBuiltinOperations; import ai.timefold.jpyinterpreter.builtins.GlobalBuiltins; import ai.timefold.jpyinterpreter.types.collections.PythonIterator; @@ -114,12 +114,12 @@ public Class findClass(String name) throws ClassNotFoundException { PythonOverloadImplementor.deferDispatchesFor(PythonLikeType::registerTypeType); try { - FUNCTION_TYPE.__dir__.put(PythonTernaryOperators.GET.dunderMethod, + FUNCTION_TYPE.__dir__.put(PythonTernaryOperator.GET.dunderMethod, new JavaMethodReference( FunctionBuiltinOperations.class.getMethod("bindFunctionToInstance", PythonLikeFunction.class, PythonLikeObject.class, PythonLikeType.class), Map.of("self", 0, "obj", 1, "objtype", 2))); - CLASS_FUNCTION_TYPE.__dir__.put(PythonTernaryOperators.GET.dunderMethod, + CLASS_FUNCTION_TYPE.__dir__.put(PythonTernaryOperator.GET.dunderMethod, new JavaMethodReference( FunctionBuiltinOperations.class.getMethod("bindFunctionToType", PythonLikeFunction.class, PythonLikeObject.class, PythonLikeType.class), diff --git a/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/PythonByteArray.java b/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/PythonByteArray.java index 7e7c5f46..08cd1905 100644 --- a/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/PythonByteArray.java +++ b/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/PythonByteArray.java @@ -13,10 +13,10 @@ import java.util.stream.Collectors; import java.util.stream.IntStream; -import ai.timefold.jpyinterpreter.PythonBinaryOperators; +import ai.timefold.jpyinterpreter.PythonBinaryOperator; import ai.timefold.jpyinterpreter.PythonLikeObject; import ai.timefold.jpyinterpreter.PythonOverloadImplementor; -import ai.timefold.jpyinterpreter.PythonTernaryOperators; +import ai.timefold.jpyinterpreter.PythonTernaryOperator; import ai.timefold.jpyinterpreter.PythonUnaryOperator; import ai.timefold.jpyinterpreter.builtins.UnaryDunderBuiltin; import ai.timefold.jpyinterpreter.types.collections.PythonIterator; @@ -77,35 +77,35 @@ private static PythonLikeType registerMethods() throws NoSuchMethodException { BuiltinTypes.BYTE_ARRAY_TYPE.addUnaryMethod(PythonUnaryOperator.LENGTH, PythonByteArray.class.getMethod("getLength")); // Binary - BuiltinTypes.BYTE_ARRAY_TYPE.addBinaryMethod(PythonBinaryOperators.GET_ITEM, + BuiltinTypes.BYTE_ARRAY_TYPE.addBinaryMethod(PythonBinaryOperator.GET_ITEM, PythonByteArray.class.getMethod("getCharAt", PythonInteger.class)); - BuiltinTypes.BYTE_ARRAY_TYPE.addBinaryMethod(PythonBinaryOperators.GET_ITEM, + BuiltinTypes.BYTE_ARRAY_TYPE.addBinaryMethod(PythonBinaryOperator.GET_ITEM, PythonByteArray.class.getMethod("getSubsequence", PythonSlice.class)); - BuiltinTypes.BYTE_ARRAY_TYPE.addBinaryMethod(PythonBinaryOperators.DELETE_ITEM, + BuiltinTypes.BYTE_ARRAY_TYPE.addBinaryMethod(PythonBinaryOperator.DELETE_ITEM, PythonByteArray.class.getMethod("deleteIndex", PythonInteger.class)); - BuiltinTypes.BYTE_ARRAY_TYPE.addBinaryMethod(PythonBinaryOperators.DELETE_ITEM, + BuiltinTypes.BYTE_ARRAY_TYPE.addBinaryMethod(PythonBinaryOperator.DELETE_ITEM, PythonByteArray.class.getMethod("deleteSlice", PythonSlice.class)); - BuiltinTypes.BYTE_ARRAY_TYPE.addBinaryMethod(PythonBinaryOperators.CONTAINS, + BuiltinTypes.BYTE_ARRAY_TYPE.addBinaryMethod(PythonBinaryOperator.CONTAINS, PythonByteArray.class.getMethod("containsSubsequence", PythonByteArray.class)); - BuiltinTypes.BYTE_ARRAY_TYPE.addBinaryMethod(PythonBinaryOperators.ADD, + BuiltinTypes.BYTE_ARRAY_TYPE.addBinaryMethod(PythonBinaryOperator.ADD, PythonByteArray.class.getMethod("concat", PythonByteArray.class)); - BuiltinTypes.BYTE_ARRAY_TYPE.addBinaryMethod(PythonBinaryOperators.INPLACE_ADD, + BuiltinTypes.BYTE_ARRAY_TYPE.addBinaryMethod(PythonBinaryOperator.INPLACE_ADD, PythonByteArray.class.getMethod("inplaceAdd", PythonLikeObject.class)); - BuiltinTypes.BYTE_ARRAY_TYPE.addBinaryMethod(PythonBinaryOperators.MULTIPLY, + BuiltinTypes.BYTE_ARRAY_TYPE.addBinaryMethod(PythonBinaryOperator.MULTIPLY, PythonByteArray.class.getMethod("repeat", PythonInteger.class)); - BuiltinTypes.BYTE_ARRAY_TYPE.addBinaryMethod(PythonBinaryOperators.INPLACE_MULTIPLY, + BuiltinTypes.BYTE_ARRAY_TYPE.addBinaryMethod(PythonBinaryOperator.INPLACE_MULTIPLY, PythonByteArray.class.getMethod("inplaceRepeat", PythonInteger.class)); - BuiltinTypes.BYTE_ARRAY_TYPE.addBinaryMethod(PythonBinaryOperators.MODULO, + BuiltinTypes.BYTE_ARRAY_TYPE.addBinaryMethod(PythonBinaryOperator.MODULO, PythonByteArray.class.getMethod("interpolate", PythonLikeObject.class)); - BuiltinTypes.BYTE_ARRAY_TYPE.addBinaryMethod(PythonBinaryOperators.MODULO, + BuiltinTypes.BYTE_ARRAY_TYPE.addBinaryMethod(PythonBinaryOperator.MODULO, PythonByteArray.class.getMethod("interpolate", PythonLikeTuple.class)); - BuiltinTypes.BYTE_ARRAY_TYPE.addBinaryMethod(PythonBinaryOperators.MODULO, + BuiltinTypes.BYTE_ARRAY_TYPE.addBinaryMethod(PythonBinaryOperator.MODULO, PythonByteArray.class.getMethod("interpolate", PythonLikeDict.class)); // Ternary - BuiltinTypes.BYTE_ARRAY_TYPE.addTernaryMethod(PythonTernaryOperators.SET_ITEM, + BuiltinTypes.BYTE_ARRAY_TYPE.addTernaryMethod(PythonTernaryOperator.SET_ITEM, PythonByteArray.class.getMethod("setByte", PythonInteger.class, PythonInteger.class)); - BuiltinTypes.BYTE_ARRAY_TYPE.addTernaryMethod(PythonTernaryOperators.SET_ITEM, + BuiltinTypes.BYTE_ARRAY_TYPE.addTernaryMethod(PythonTernaryOperator.SET_ITEM, PythonByteArray.class.getMethod("setSlice", PythonSlice.class, PythonLikeObject.class)); // Other diff --git a/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/PythonBytes.java b/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/PythonBytes.java index f80d109e..5fa362d8 100644 --- a/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/PythonBytes.java +++ b/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/PythonBytes.java @@ -13,7 +13,7 @@ import java.util.stream.Collectors; import java.util.stream.IntStream; -import ai.timefold.jpyinterpreter.PythonBinaryOperators; +import ai.timefold.jpyinterpreter.PythonBinaryOperator; import ai.timefold.jpyinterpreter.PythonLikeObject; import ai.timefold.jpyinterpreter.PythonOverloadImplementor; import ai.timefold.jpyinterpreter.PythonUnaryOperator; @@ -159,21 +159,21 @@ private static PythonLikeType registerMethods() throws NoSuchMethodException { BuiltinTypes.BYTES_TYPE.addUnaryMethod(PythonUnaryOperator.LENGTH, PythonBytes.class.getMethod("getLength")); // Binary - BuiltinTypes.BYTES_TYPE.addBinaryMethod(PythonBinaryOperators.GET_ITEM, + BuiltinTypes.BYTES_TYPE.addBinaryMethod(PythonBinaryOperator.GET_ITEM, PythonBytes.class.getMethod("getCharAt", PythonInteger.class)); - BuiltinTypes.BYTES_TYPE.addBinaryMethod(PythonBinaryOperators.GET_ITEM, + BuiltinTypes.BYTES_TYPE.addBinaryMethod(PythonBinaryOperator.GET_ITEM, PythonBytes.class.getMethod("getSubsequence", PythonSlice.class)); - BuiltinTypes.BYTES_TYPE.addBinaryMethod(PythonBinaryOperators.CONTAINS, + BuiltinTypes.BYTES_TYPE.addBinaryMethod(PythonBinaryOperator.CONTAINS, PythonBytes.class.getMethod("containsSubsequence", PythonBytes.class)); - BuiltinTypes.BYTES_TYPE.addBinaryMethod(PythonBinaryOperators.ADD, + BuiltinTypes.BYTES_TYPE.addBinaryMethod(PythonBinaryOperator.ADD, PythonBytes.class.getMethod("concat", PythonBytes.class)); - BuiltinTypes.BYTES_TYPE.addBinaryMethod(PythonBinaryOperators.MULTIPLY, + BuiltinTypes.BYTES_TYPE.addBinaryMethod(PythonBinaryOperator.MULTIPLY, PythonBytes.class.getMethod("repeat", PythonInteger.class)); - BuiltinTypes.BYTES_TYPE.addBinaryMethod(PythonBinaryOperators.MODULO, + BuiltinTypes.BYTES_TYPE.addBinaryMethod(PythonBinaryOperator.MODULO, PythonBytes.class.getMethod("interpolate", PythonLikeObject.class)); - BuiltinTypes.BYTES_TYPE.addBinaryMethod(PythonBinaryOperators.MODULO, + BuiltinTypes.BYTES_TYPE.addBinaryMethod(PythonBinaryOperator.MODULO, PythonBytes.class.getMethod("interpolate", PythonLikeTuple.class)); - BuiltinTypes.BYTES_TYPE.addBinaryMethod(PythonBinaryOperators.MODULO, + BuiltinTypes.BYTES_TYPE.addBinaryMethod(PythonBinaryOperator.MODULO, PythonBytes.class.getMethod("interpolate", PythonLikeDict.class)); // Other diff --git a/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/PythonLikeComparable.java b/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/PythonLikeComparable.java index 1c4080f7..8dc287ed 100644 --- a/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/PythonLikeComparable.java +++ b/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/PythonLikeComparable.java @@ -1,23 +1,23 @@ package ai.timefold.jpyinterpreter.types; import ai.timefold.jpyinterpreter.MethodDescriptor; -import ai.timefold.jpyinterpreter.PythonBinaryOperators; +import ai.timefold.jpyinterpreter.PythonBinaryOperator; import ai.timefold.jpyinterpreter.PythonFunctionSignature; import ai.timefold.jpyinterpreter.types.numeric.PythonBoolean; public interface PythonLikeComparable extends Comparable { static void setup(PythonLikeType type) { try { - type.addBinaryMethod(PythonBinaryOperators.LESS_THAN, new PythonFunctionSignature( + type.addBinaryMethod(PythonBinaryOperator.LESS_THAN, new PythonFunctionSignature( new MethodDescriptor(PythonLikeComparable.class.getMethod("lessThan", Object.class)), BuiltinTypes.BOOLEAN_TYPE, type)); - type.addBinaryMethod(PythonBinaryOperators.GREATER_THAN, new PythonFunctionSignature( + type.addBinaryMethod(PythonBinaryOperator.GREATER_THAN, new PythonFunctionSignature( new MethodDescriptor(PythonLikeComparable.class.getMethod("greaterThan", Object.class)), BuiltinTypes.BOOLEAN_TYPE, type)); - type.addBinaryMethod(PythonBinaryOperators.LESS_THAN_OR_EQUAL, new PythonFunctionSignature( + type.addBinaryMethod(PythonBinaryOperator.LESS_THAN_OR_EQUAL, new PythonFunctionSignature( new MethodDescriptor(PythonLikeComparable.class.getMethod("lessThanOrEqual", Object.class)), BuiltinTypes.BOOLEAN_TYPE, type)); - type.addBinaryMethod(PythonBinaryOperators.GREATER_THAN_OR_EQUAL, new PythonFunctionSignature( + type.addBinaryMethod(PythonBinaryOperator.GREATER_THAN_OR_EQUAL, new PythonFunctionSignature( new MethodDescriptor(PythonLikeComparable.class.getMethod("greaterThanOrEqual", Object.class)), BuiltinTypes.BOOLEAN_TYPE, type)); } catch (NoSuchMethodException e) { diff --git a/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/PythonLikeType.java b/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/PythonLikeType.java index d885ef5a..dcb88c62 100644 --- a/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/PythonLikeType.java +++ b/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/PythonLikeType.java @@ -14,12 +14,12 @@ import java.util.stream.Stream; import ai.timefold.jpyinterpreter.FieldDescriptor; -import ai.timefold.jpyinterpreter.PythonBinaryOperators; +import ai.timefold.jpyinterpreter.PythonBinaryOperator; import ai.timefold.jpyinterpreter.PythonClassTranslator; import ai.timefold.jpyinterpreter.PythonFunctionSignature; import ai.timefold.jpyinterpreter.PythonLikeObject; import ai.timefold.jpyinterpreter.PythonOverloadImplementor; -import ai.timefold.jpyinterpreter.PythonTernaryOperators; +import ai.timefold.jpyinterpreter.PythonTernaryOperator; import ai.timefold.jpyinterpreter.PythonUnaryOperator; import ai.timefold.jpyinterpreter.builtins.TernaryDunderBuiltin; import ai.timefold.jpyinterpreter.types.collections.PythonLikeDict; @@ -130,15 +130,15 @@ public boolean isInstance(PythonLikeObject object) { public static PythonLikeType registerBaseType() { try { - BuiltinTypes.BASE_TYPE.addBinaryMethod(PythonBinaryOperators.GET_ATTRIBUTE, + BuiltinTypes.BASE_TYPE.addBinaryMethod(PythonBinaryOperator.GET_ATTRIBUTE, PythonLikeObject.class.getMethod("$method$__getattribute__", PythonString.class)); - BuiltinTypes.BASE_TYPE.addTernaryMethod(PythonTernaryOperators.SET_ATTRIBUTE, + BuiltinTypes.BASE_TYPE.addTernaryMethod(PythonTernaryOperator.SET_ATTRIBUTE, PythonLikeObject.class.getMethod("$method$__setattr__", PythonString.class, PythonLikeObject.class)); - BuiltinTypes.BASE_TYPE.addBinaryMethod(PythonBinaryOperators.DELETE_ATTRIBUTE, + BuiltinTypes.BASE_TYPE.addBinaryMethod(PythonBinaryOperator.DELETE_ATTRIBUTE, PythonLikeObject.class.getMethod("$method$__delattr__", PythonString.class)); - BuiltinTypes.BASE_TYPE.addBinaryMethod(PythonBinaryOperators.EQUAL, + BuiltinTypes.BASE_TYPE.addBinaryMethod(PythonBinaryOperator.EQUAL, PythonLikeObject.class.getMethod("$method$__eq__", PythonLikeObject.class)); - BuiltinTypes.BASE_TYPE.addBinaryMethod(PythonBinaryOperators.NOT_EQUAL, + BuiltinTypes.BASE_TYPE.addBinaryMethod(PythonBinaryOperator.NOT_EQUAL, PythonLikeObject.class.getMethod("$method$__ne__", PythonLikeObject.class)); BuiltinTypes.BASE_TYPE.addUnaryMethod(PythonUnaryOperator.AS_STRING, PythonLikeObject.class.getMethod("$method$__str__")); @@ -146,7 +146,7 @@ public static PythonLikeType registerBaseType() { PythonLikeObject.class.getMethod("$method$__repr__")); BuiltinTypes.BASE_TYPE.addUnaryMethod(PythonUnaryOperator.HASH, PythonLikeObject.class.getMethod("$method$__hash__")); - BuiltinTypes.BASE_TYPE.addBinaryMethod(PythonBinaryOperators.FORMAT, + BuiltinTypes.BASE_TYPE.addBinaryMethod(PythonBinaryOperator.FORMAT, PythonLikeObject.class.getMethod("$method$__format__", PythonLikeObject.class)); BuiltinTypes.BASE_TYPE .setConstructor((vargs, kwargs, callerInstance) -> new AbstractPythonLikeObject(BuiltinTypes.BASE_TYPE) { @@ -195,9 +195,9 @@ public static PythonLikeType registerTypeType() { String name = pythonName.value; PythonLikeObject typeResult = __getAttributeOrNull(name); if (typeResult != null) { - PythonLikeObject maybeDescriptor = typeResult.__getAttributeOrNull(PythonTernaryOperators.GET.dunderMethod); + PythonLikeObject maybeDescriptor = typeResult.__getAttributeOrNull(PythonTernaryOperator.GET.dunderMethod); if (maybeDescriptor == null) { - maybeDescriptor = typeResult.__getType().__getAttributeOrNull(PythonTernaryOperators.GET.dunderMethod); + maybeDescriptor = typeResult.__getType().__getAttributeOrNull(PythonTernaryOperator.GET.dunderMethod); } if (maybeDescriptor != null) { @@ -220,22 +220,22 @@ public void addUnaryMethod(PythonUnaryOperator operator, Method method) { addMethod(operator.getDunderMethod(), PythonFunctionSignature.forMethod(method)); } - public void addBinaryMethod(PythonBinaryOperators operator, Method method) { + public void addBinaryMethod(PythonBinaryOperator operator, Method method) { addMethod(operator.getDunderMethod(), PythonFunctionSignature.forMethod(method)); if (operator.hasRightDunderMethod() && !operator.isComparisonMethod()) { addMethod(operator.getRightDunderMethod(), PythonFunctionSignature.forMethod(method)); } } - public void addLeftBinaryMethod(PythonBinaryOperators operator, Method method) { + public void addLeftBinaryMethod(PythonBinaryOperator operator, Method method) { addMethod(operator.getDunderMethod(), PythonFunctionSignature.forMethod(method)); } - public void addRightBinaryMethod(PythonBinaryOperators operator, Method method) { + public void addRightBinaryMethod(PythonBinaryOperator operator, Method method) { addMethod(operator.getRightDunderMethod(), PythonFunctionSignature.forMethod(method)); } - public void addTernaryMethod(PythonTernaryOperators operator, Method method) { + public void addTernaryMethod(PythonTernaryOperator operator, Method method) { addMethod(operator.getDunderMethod(), PythonFunctionSignature.forMethod(method)); } @@ -243,22 +243,22 @@ public void addUnaryMethod(PythonUnaryOperator operator, PythonFunctionSignature addMethod(operator.getDunderMethod(), method); } - public void addBinaryMethod(PythonBinaryOperators operator, PythonFunctionSignature method) { + public void addBinaryMethod(PythonBinaryOperator operator, PythonFunctionSignature method) { addMethod(operator.getDunderMethod(), method); if (operator.hasRightDunderMethod() && !operator.isComparisonMethod()) { addMethod(operator.getRightDunderMethod(), method); } } - public void addLeftBinaryMethod(PythonBinaryOperators operator, PythonFunctionSignature method) { + public void addLeftBinaryMethod(PythonBinaryOperator operator, PythonFunctionSignature method) { addMethod(operator.getDunderMethod(), method); } - public void addRightBinaryMethod(PythonBinaryOperators operator, PythonFunctionSignature method) { + public void addRightBinaryMethod(PythonBinaryOperator operator, PythonFunctionSignature method) { addMethod(operator.getRightDunderMethod(), method); } - public void addTernaryMethod(PythonTernaryOperators operator, PythonFunctionSignature method) { + public void addTernaryMethod(PythonTernaryOperator operator, PythonFunctionSignature method) { addMethod(operator.getDunderMethod(), method); } diff --git a/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/PythonNone.java b/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/PythonNone.java index 05f6e1e2..90c8fe80 100644 --- a/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/PythonNone.java +++ b/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/PythonNone.java @@ -1,6 +1,6 @@ package ai.timefold.jpyinterpreter.types; -import ai.timefold.jpyinterpreter.PythonBinaryOperators; +import ai.timefold.jpyinterpreter.PythonBinaryOperator; import ai.timefold.jpyinterpreter.PythonLikeObject; import ai.timefold.jpyinterpreter.PythonOverloadImplementor; import ai.timefold.jpyinterpreter.PythonUnaryOperator; @@ -17,9 +17,9 @@ public class PythonNone extends AbstractPythonLikeObject { private static PythonLikeType registerMethods() throws NoSuchMethodException { BuiltinTypes.NONE_TYPE.addUnaryMethod(PythonUnaryOperator.AS_BOOLEAN, PythonNone.class.getMethod("asBool")); - BuiltinTypes.NONE_TYPE.addBinaryMethod(PythonBinaryOperators.EQUAL, + BuiltinTypes.NONE_TYPE.addBinaryMethod(PythonBinaryOperator.EQUAL, PythonNone.class.getMethod("equalsObject", PythonLikeObject.class)); - BuiltinTypes.NONE_TYPE.addBinaryMethod(PythonBinaryOperators.NOT_EQUAL, + BuiltinTypes.NONE_TYPE.addBinaryMethod(PythonBinaryOperator.NOT_EQUAL, PythonNone.class.getMethod("notEqualsObject", PythonLikeObject.class)); GlobalBuiltins.addBuiltinConstant("None", INSTANCE); return BuiltinTypes.NONE_TYPE; diff --git a/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/PythonRange.java b/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/PythonRange.java index 20aad5f8..7ded913b 100644 --- a/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/PythonRange.java +++ b/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/PythonRange.java @@ -8,7 +8,7 @@ import java.util.ListIterator; import java.util.Map; -import ai.timefold.jpyinterpreter.PythonBinaryOperators; +import ai.timefold.jpyinterpreter.PythonBinaryOperator; import ai.timefold.jpyinterpreter.PythonLikeObject; import ai.timefold.jpyinterpreter.PythonOverloadImplementor; import ai.timefold.jpyinterpreter.PythonUnaryOperator; @@ -90,9 +90,9 @@ private static PythonLikeType registerMethods() throws NoSuchMethodException { BuiltinTypes.RANGE_TYPE.addUnaryMethod(PythonUnaryOperator.ITERATOR, PythonRange.class.getMethod("getPythonIterator")); // Binary methods - BuiltinTypes.RANGE_TYPE.addBinaryMethod(PythonBinaryOperators.GET_ITEM, + BuiltinTypes.RANGE_TYPE.addBinaryMethod(PythonBinaryOperator.GET_ITEM, PythonRange.class.getMethod("getItem", PythonInteger.class)); - BuiltinTypes.RANGE_TYPE.addBinaryMethod(PythonBinaryOperators.CONTAINS, + BuiltinTypes.RANGE_TYPE.addBinaryMethod(PythonBinaryOperator.CONTAINS, PythonRange.class.getMethod("isObjectInRange", PythonLikeObject.class)); return BuiltinTypes.RANGE_TYPE; diff --git a/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/PythonSlice.java b/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/PythonSlice.java index d0282aa1..59172bc7 100644 --- a/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/PythonSlice.java +++ b/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/PythonSlice.java @@ -5,7 +5,7 @@ import java.util.Map; import java.util.Objects; -import ai.timefold.jpyinterpreter.PythonBinaryOperators; +import ai.timefold.jpyinterpreter.PythonBinaryOperator; import ai.timefold.jpyinterpreter.PythonLikeObject; import ai.timefold.jpyinterpreter.PythonOverloadImplementor; import ai.timefold.jpyinterpreter.PythonUnaryOperator; @@ -67,7 +67,7 @@ private static PythonLikeType registerMethods() throws NoSuchMethodException { SLICE_TYPE.addUnaryMethod(PythonUnaryOperator.HASH, PythonSlice.class.getMethod("pythonHash")); // Binary - SLICE_TYPE.addBinaryMethod(PythonBinaryOperators.EQUAL, PythonSlice.class.getMethod("pythonEquals", PythonSlice.class)); + SLICE_TYPE.addBinaryMethod(PythonBinaryOperator.EQUAL, PythonSlice.class.getMethod("pythonEquals", PythonSlice.class)); // Other methods SLICE_TYPE.addMethod("indices", PythonSlice.class.getMethod("indices", PythonInteger.class)); diff --git a/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/PythonString.java b/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/PythonString.java index 0834b6c3..98c2a9d2 100644 --- a/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/PythonString.java +++ b/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/PythonString.java @@ -16,7 +16,7 @@ import java.util.stream.Collectors; import java.util.stream.IntStream; -import ai.timefold.jpyinterpreter.PythonBinaryOperators; +import ai.timefold.jpyinterpreter.PythonBinaryOperator; import ai.timefold.jpyinterpreter.PythonLikeObject; import ai.timefold.jpyinterpreter.PythonOverloadImplementor; import ai.timefold.jpyinterpreter.PythonUnaryOperator; @@ -78,24 +78,24 @@ private static PythonLikeType registerMethods() throws NoSuchMethodException { BuiltinTypes.STRING_TYPE.addUnaryMethod(PythonUnaryOperator.LENGTH, PythonString.class.getMethod("getLength")); // Binary - BuiltinTypes.STRING_TYPE.addBinaryMethod(PythonBinaryOperators.GET_ITEM, + BuiltinTypes.STRING_TYPE.addBinaryMethod(PythonBinaryOperator.GET_ITEM, PythonString.class.getMethod("getCharAt", PythonInteger.class)); - BuiltinTypes.STRING_TYPE.addBinaryMethod(PythonBinaryOperators.GET_ITEM, + BuiltinTypes.STRING_TYPE.addBinaryMethod(PythonBinaryOperator.GET_ITEM, PythonString.class.getMethod("getSubstring", PythonSlice.class)); - BuiltinTypes.STRING_TYPE.addBinaryMethod(PythonBinaryOperators.CONTAINS, + BuiltinTypes.STRING_TYPE.addBinaryMethod(PythonBinaryOperator.CONTAINS, PythonString.class.getMethod("containsSubstring", PythonString.class)); - BuiltinTypes.STRING_TYPE.addBinaryMethod(PythonBinaryOperators.ADD, + BuiltinTypes.STRING_TYPE.addBinaryMethod(PythonBinaryOperator.ADD, PythonString.class.getMethod("concat", PythonString.class)); - BuiltinTypes.STRING_TYPE.addBinaryMethod(PythonBinaryOperators.MULTIPLY, + BuiltinTypes.STRING_TYPE.addBinaryMethod(PythonBinaryOperator.MULTIPLY, PythonString.class.getMethod("repeat", PythonInteger.class)); - BuiltinTypes.STRING_TYPE.addBinaryMethod(PythonBinaryOperators.MODULO, + BuiltinTypes.STRING_TYPE.addBinaryMethod(PythonBinaryOperator.MODULO, PythonString.class.getMethod("interpolate", PythonLikeObject.class)); - BuiltinTypes.STRING_TYPE.addBinaryMethod(PythonBinaryOperators.MODULO, + BuiltinTypes.STRING_TYPE.addBinaryMethod(PythonBinaryOperator.MODULO, PythonString.class.getMethod("interpolate", PythonLikeTuple.class)); - BuiltinTypes.STRING_TYPE.addBinaryMethod(PythonBinaryOperators.MODULO, + BuiltinTypes.STRING_TYPE.addBinaryMethod(PythonBinaryOperator.MODULO, PythonString.class.getMethod("interpolate", PythonLikeDict.class)); - BuiltinTypes.STRING_TYPE.addBinaryMethod(PythonBinaryOperators.FORMAT, PythonString.class.getMethod("formatSelf")); - BuiltinTypes.STRING_TYPE.addBinaryMethod(PythonBinaryOperators.FORMAT, + BuiltinTypes.STRING_TYPE.addBinaryMethod(PythonBinaryOperator.FORMAT, PythonString.class.getMethod("formatSelf")); + BuiltinTypes.STRING_TYPE.addBinaryMethod(PythonBinaryOperator.FORMAT, PythonString.class.getMethod("formatSelf", PythonString.class)); // Other diff --git a/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/PythonSuperObject.java b/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/PythonSuperObject.java index f4a5c13b..ec93fbf6 100644 --- a/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/PythonSuperObject.java +++ b/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/PythonSuperObject.java @@ -5,7 +5,7 @@ import ai.timefold.jpyinterpreter.PythonClassTranslator; import ai.timefold.jpyinterpreter.PythonLikeObject; -import ai.timefold.jpyinterpreter.PythonTernaryOperators; +import ai.timefold.jpyinterpreter.PythonTernaryOperator; import ai.timefold.jpyinterpreter.builtins.TernaryDunderBuiltin; import ai.timefold.jpyinterpreter.types.errors.AttributeError; @@ -60,9 +60,9 @@ public PythonSuperObject(PythonLikeType previousType, PythonLikeObject instance) } } - PythonLikeObject maybeDescriptor = typeResult.__getAttributeOrNull(PythonTernaryOperators.GET.dunderMethod); + PythonLikeObject maybeDescriptor = typeResult.__getAttributeOrNull(PythonTernaryOperator.GET.dunderMethod); if (maybeDescriptor == null) { - maybeDescriptor = typeResult.__getType().__getAttributeOrNull(PythonTernaryOperators.GET.dunderMethod); + maybeDescriptor = typeResult.__getType().__getAttributeOrNull(PythonTernaryOperator.GET.dunderMethod); } if (maybeDescriptor != null) { diff --git a/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/collections/PythonLikeDict.java b/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/collections/PythonLikeDict.java index 59afddab..8893913a 100644 --- a/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/collections/PythonLikeDict.java +++ b/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/collections/PythonLikeDict.java @@ -10,10 +10,10 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import ai.timefold.jpyinterpreter.PythonBinaryOperators; +import ai.timefold.jpyinterpreter.PythonBinaryOperator; import ai.timefold.jpyinterpreter.PythonLikeObject; import ai.timefold.jpyinterpreter.PythonOverloadImplementor; -import ai.timefold.jpyinterpreter.PythonTernaryOperators; +import ai.timefold.jpyinterpreter.PythonTernaryOperator; import ai.timefold.jpyinterpreter.PythonUnaryOperator; import ai.timefold.jpyinterpreter.builtins.UnaryDunderBuiltin; import ai.timefold.jpyinterpreter.types.AbstractPythonLikeObject; @@ -70,19 +70,19 @@ private static PythonLikeType registerMethods() throws NoSuchMethodException { BuiltinTypes.DICT_TYPE.addUnaryMethod(PythonUnaryOperator.REVERSED, PythonLikeDict.class.getMethod("reversed")); // Binary operators - BuiltinTypes.DICT_TYPE.addBinaryMethod(PythonBinaryOperators.GET_ITEM, + BuiltinTypes.DICT_TYPE.addBinaryMethod(PythonBinaryOperator.GET_ITEM, PythonLikeDict.class.getMethod("getItemOrError", PythonLikeObject.class)); - BuiltinTypes.DICT_TYPE.addBinaryMethod(PythonBinaryOperators.DELETE_ITEM, + BuiltinTypes.DICT_TYPE.addBinaryMethod(PythonBinaryOperator.DELETE_ITEM, PythonLikeDict.class.getMethod("removeItemOrError", PythonLikeObject.class)); - BuiltinTypes.DICT_TYPE.addBinaryMethod(PythonBinaryOperators.CONTAINS, + BuiltinTypes.DICT_TYPE.addBinaryMethod(PythonBinaryOperator.CONTAINS, PythonLikeDict.class.getMethod("isKeyInDict", PythonLikeObject.class)); - BuiltinTypes.DICT_TYPE.addBinaryMethod(PythonBinaryOperators.OR, + BuiltinTypes.DICT_TYPE.addBinaryMethod(PythonBinaryOperator.OR, PythonLikeDict.class.getMethod("binaryOr", PythonLikeDict.class)); - BuiltinTypes.DICT_TYPE.addBinaryMethod(PythonBinaryOperators.INPLACE_OR, + BuiltinTypes.DICT_TYPE.addBinaryMethod(PythonBinaryOperator.INPLACE_OR, PythonLikeDict.class.getMethod("binaryInplaceOr", PythonLikeDict.class)); // Ternary operators - BuiltinTypes.DICT_TYPE.addTernaryMethod(PythonTernaryOperators.SET_ITEM, + BuiltinTypes.DICT_TYPE.addTernaryMethod(PythonTernaryOperator.SET_ITEM, PythonLikeDict.class.getMethod("setItem", PythonLikeObject.class, PythonLikeObject.class)); // Other diff --git a/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/collections/PythonLikeFrozenSet.java b/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/collections/PythonLikeFrozenSet.java index 4ba64e32..61ac2c69 100644 --- a/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/collections/PythonLikeFrozenSet.java +++ b/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/collections/PythonLikeFrozenSet.java @@ -8,7 +8,7 @@ import java.util.Set; import java.util.function.Predicate; -import ai.timefold.jpyinterpreter.PythonBinaryOperators; +import ai.timefold.jpyinterpreter.PythonBinaryOperator; import ai.timefold.jpyinterpreter.PythonLikeObject; import ai.timefold.jpyinterpreter.PythonOverloadImplementor; import ai.timefold.jpyinterpreter.PythonUnaryOperator; @@ -47,7 +47,7 @@ private static PythonLikeType registerMethods() throws NoSuchMethodException { PythonLikeFrozenSet.class.getMethod("getIterator")); // Binary - BuiltinTypes.FROZEN_SET_TYPE.addBinaryMethod(PythonBinaryOperators.CONTAINS, + BuiltinTypes.FROZEN_SET_TYPE.addBinaryMethod(PythonBinaryOperator.CONTAINS, PythonLikeFrozenSet.class.getMethod("containsItem", PythonLikeObject.class)); // Other @@ -58,63 +58,63 @@ private static PythonLikeType registerMethods() throws NoSuchMethodException { BuiltinTypes.FROZEN_SET_TYPE.addMethod("issubset", PythonLikeFrozenSet.class.getMethod("isSubset", PythonLikeSet.class)); - BuiltinTypes.FROZEN_SET_TYPE.addBinaryMethod(PythonBinaryOperators.LESS_THAN_OR_EQUAL, + BuiltinTypes.FROZEN_SET_TYPE.addBinaryMethod(PythonBinaryOperator.LESS_THAN_OR_EQUAL, PythonLikeFrozenSet.class.getMethod("isSubset", PythonLikeSet.class)); - BuiltinTypes.FROZEN_SET_TYPE.addBinaryMethod(PythonBinaryOperators.LESS_THAN, + BuiltinTypes.FROZEN_SET_TYPE.addBinaryMethod(PythonBinaryOperator.LESS_THAN, PythonLikeFrozenSet.class.getMethod("isStrictSubset", PythonLikeSet.class)); BuiltinTypes.FROZEN_SET_TYPE.addMethod("issubset", PythonLikeFrozenSet.class.getMethod("isSubset", PythonLikeFrozenSet.class)); - BuiltinTypes.FROZEN_SET_TYPE.addBinaryMethod(PythonBinaryOperators.LESS_THAN_OR_EQUAL, + BuiltinTypes.FROZEN_SET_TYPE.addBinaryMethod(PythonBinaryOperator.LESS_THAN_OR_EQUAL, PythonLikeFrozenSet.class.getMethod("isSubset", PythonLikeFrozenSet.class)); - BuiltinTypes.FROZEN_SET_TYPE.addBinaryMethod(PythonBinaryOperators.LESS_THAN, + BuiltinTypes.FROZEN_SET_TYPE.addBinaryMethod(PythonBinaryOperator.LESS_THAN, PythonLikeFrozenSet.class.getMethod("isStrictSubset", PythonLikeFrozenSet.class)); BuiltinTypes.FROZEN_SET_TYPE.addMethod("issuperset", PythonLikeFrozenSet.class.getMethod("isSuperset", PythonLikeSet.class)); - BuiltinTypes.FROZEN_SET_TYPE.addBinaryMethod(PythonBinaryOperators.GREATER_THAN_OR_EQUAL, + BuiltinTypes.FROZEN_SET_TYPE.addBinaryMethod(PythonBinaryOperator.GREATER_THAN_OR_EQUAL, PythonLikeFrozenSet.class.getMethod("isSuperset", PythonLikeSet.class)); - BuiltinTypes.FROZEN_SET_TYPE.addBinaryMethod(PythonBinaryOperators.GREATER_THAN, + BuiltinTypes.FROZEN_SET_TYPE.addBinaryMethod(PythonBinaryOperator.GREATER_THAN, PythonLikeFrozenSet.class.getMethod("isStrictSuperset", PythonLikeSet.class)); BuiltinTypes.FROZEN_SET_TYPE.addMethod("issuperset", PythonLikeFrozenSet.class.getMethod("isSuperset", PythonLikeFrozenSet.class)); - BuiltinTypes.FROZEN_SET_TYPE.addBinaryMethod(PythonBinaryOperators.GREATER_THAN_OR_EQUAL, + BuiltinTypes.FROZEN_SET_TYPE.addBinaryMethod(PythonBinaryOperator.GREATER_THAN_OR_EQUAL, PythonLikeFrozenSet.class.getMethod("isSuperset", PythonLikeFrozenSet.class)); - BuiltinTypes.FROZEN_SET_TYPE.addBinaryMethod(PythonBinaryOperators.GREATER_THAN, + BuiltinTypes.FROZEN_SET_TYPE.addBinaryMethod(PythonBinaryOperator.GREATER_THAN, PythonLikeFrozenSet.class.getMethod("isStrictSuperset", PythonLikeFrozenSet.class)); BuiltinTypes.FROZEN_SET_TYPE.addMethod("union", PythonLikeFrozenSet.class.getMethod("union", PythonLikeSet.class)); BuiltinTypes.FROZEN_SET_TYPE.addMethod("union", PythonLikeFrozenSet.class.getMethod("union", PythonLikeFrozenSet.class)); - BuiltinTypes.FROZEN_SET_TYPE.addBinaryMethod(PythonBinaryOperators.OR, + BuiltinTypes.FROZEN_SET_TYPE.addBinaryMethod(PythonBinaryOperator.OR, PythonLikeFrozenSet.class.getMethod("union", PythonLikeSet.class)); - BuiltinTypes.FROZEN_SET_TYPE.addBinaryMethod(PythonBinaryOperators.OR, + BuiltinTypes.FROZEN_SET_TYPE.addBinaryMethod(PythonBinaryOperator.OR, PythonLikeFrozenSet.class.getMethod("union", PythonLikeFrozenSet.class)); BuiltinTypes.FROZEN_SET_TYPE.addMethod("intersection", PythonLikeFrozenSet.class.getMethod("intersection", PythonLikeSet.class)); BuiltinTypes.FROZEN_SET_TYPE.addMethod("intersection", PythonLikeFrozenSet.class.getMethod("intersection", PythonLikeFrozenSet.class)); - BuiltinTypes.FROZEN_SET_TYPE.addBinaryMethod(PythonBinaryOperators.AND, + BuiltinTypes.FROZEN_SET_TYPE.addBinaryMethod(PythonBinaryOperator.AND, PythonLikeFrozenSet.class.getMethod("intersection", PythonLikeSet.class)); - BuiltinTypes.FROZEN_SET_TYPE.addBinaryMethod(PythonBinaryOperators.AND, + BuiltinTypes.FROZEN_SET_TYPE.addBinaryMethod(PythonBinaryOperator.AND, PythonLikeFrozenSet.class.getMethod("intersection", PythonLikeFrozenSet.class)); BuiltinTypes.FROZEN_SET_TYPE.addMethod("difference", PythonLikeFrozenSet.class.getMethod("difference", PythonLikeSet.class)); BuiltinTypes.FROZEN_SET_TYPE.addMethod("difference", PythonLikeFrozenSet.class.getMethod("difference", PythonLikeFrozenSet.class)); - BuiltinTypes.FROZEN_SET_TYPE.addBinaryMethod(PythonBinaryOperators.SUBTRACT, + BuiltinTypes.FROZEN_SET_TYPE.addBinaryMethod(PythonBinaryOperator.SUBTRACT, PythonLikeFrozenSet.class.getMethod("difference", PythonLikeSet.class)); - BuiltinTypes.FROZEN_SET_TYPE.addBinaryMethod(PythonBinaryOperators.SUBTRACT, + BuiltinTypes.FROZEN_SET_TYPE.addBinaryMethod(PythonBinaryOperator.SUBTRACT, PythonLikeFrozenSet.class.getMethod("difference", PythonLikeFrozenSet.class)); BuiltinTypes.FROZEN_SET_TYPE.addMethod("symmetric_difference", PythonLikeFrozenSet.class.getMethod("symmetricDifference", PythonLikeSet.class)); BuiltinTypes.FROZEN_SET_TYPE.addMethod("symmetric_difference", PythonLikeFrozenSet.class.getMethod("symmetricDifference", PythonLikeFrozenSet.class)); - BuiltinTypes.FROZEN_SET_TYPE.addBinaryMethod(PythonBinaryOperators.XOR, + BuiltinTypes.FROZEN_SET_TYPE.addBinaryMethod(PythonBinaryOperator.XOR, PythonLikeFrozenSet.class.getMethod("symmetricDifference", PythonLikeSet.class)); - BuiltinTypes.FROZEN_SET_TYPE.addBinaryMethod(PythonBinaryOperators.XOR, + BuiltinTypes.FROZEN_SET_TYPE.addBinaryMethod(PythonBinaryOperator.XOR, PythonLikeFrozenSet.class.getMethod("symmetricDifference", PythonLikeFrozenSet.class)); BuiltinTypes.FROZEN_SET_TYPE.addMethod("copy", PythonLikeFrozenSet.class.getMethod("copy")); diff --git a/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/collections/PythonLikeList.java b/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/collections/PythonLikeList.java index c4b58f68..f6c373a8 100644 --- a/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/collections/PythonLikeList.java +++ b/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/collections/PythonLikeList.java @@ -10,10 +10,10 @@ import java.util.Objects; import java.util.RandomAccess; -import ai.timefold.jpyinterpreter.PythonBinaryOperators; +import ai.timefold.jpyinterpreter.PythonBinaryOperator; import ai.timefold.jpyinterpreter.PythonLikeObject; import ai.timefold.jpyinterpreter.PythonOverloadImplementor; -import ai.timefold.jpyinterpreter.PythonTernaryOperators; +import ai.timefold.jpyinterpreter.PythonTernaryOperator; import ai.timefold.jpyinterpreter.PythonUnaryOperator; import ai.timefold.jpyinterpreter.builtins.UnaryDunderBuiltin; import ai.timefold.jpyinterpreter.types.AbstractPythonLikeObject; @@ -56,29 +56,29 @@ private static PythonLikeType registerMethods() throws NoSuchMethodException { PythonLikeList.class.getMethod("getRepresentation")); // Binary methods - BuiltinTypes.LIST_TYPE.addBinaryMethod(PythonBinaryOperators.ADD, + BuiltinTypes.LIST_TYPE.addBinaryMethod(PythonBinaryOperator.ADD, PythonLikeList.class.getMethod("concatToNew", PythonLikeList.class)); - BuiltinTypes.LIST_TYPE.addBinaryMethod(PythonBinaryOperators.INPLACE_ADD, + BuiltinTypes.LIST_TYPE.addBinaryMethod(PythonBinaryOperator.INPLACE_ADD, PythonLikeList.class.getMethod("concatToSelf", PythonLikeList.class)); - BuiltinTypes.LIST_TYPE.addBinaryMethod(PythonBinaryOperators.MULTIPLY, + BuiltinTypes.LIST_TYPE.addBinaryMethod(PythonBinaryOperator.MULTIPLY, PythonLikeList.class.getMethod("multiplyToNew", PythonInteger.class)); - BuiltinTypes.LIST_TYPE.addBinaryMethod(PythonBinaryOperators.INPLACE_MULTIPLY, + BuiltinTypes.LIST_TYPE.addBinaryMethod(PythonBinaryOperator.INPLACE_MULTIPLY, PythonLikeList.class.getMethod("multiplyToSelf", PythonInteger.class)); - BuiltinTypes.LIST_TYPE.addBinaryMethod(PythonBinaryOperators.GET_ITEM, + BuiltinTypes.LIST_TYPE.addBinaryMethod(PythonBinaryOperator.GET_ITEM, PythonLikeList.class.getMethod("getItem", PythonInteger.class)); - BuiltinTypes.LIST_TYPE.addBinaryMethod(PythonBinaryOperators.GET_ITEM, + BuiltinTypes.LIST_TYPE.addBinaryMethod(PythonBinaryOperator.GET_ITEM, PythonLikeList.class.getMethod("getSlice", PythonSlice.class)); - BuiltinTypes.LIST_TYPE.addBinaryMethod(PythonBinaryOperators.DELETE_ITEM, + BuiltinTypes.LIST_TYPE.addBinaryMethod(PythonBinaryOperator.DELETE_ITEM, PythonLikeList.class.getMethod("deleteItem", PythonInteger.class)); - BuiltinTypes.LIST_TYPE.addBinaryMethod(PythonBinaryOperators.DELETE_ITEM, + BuiltinTypes.LIST_TYPE.addBinaryMethod(PythonBinaryOperator.DELETE_ITEM, PythonLikeList.class.getMethod("deleteSlice", PythonSlice.class)); - BuiltinTypes.LIST_TYPE.addBinaryMethod(PythonBinaryOperators.CONTAINS, + BuiltinTypes.LIST_TYPE.addBinaryMethod(PythonBinaryOperator.CONTAINS, PythonLikeList.class.getMethod("containsItem", PythonLikeObject.class)); // Ternary methods - BuiltinTypes.LIST_TYPE.addTernaryMethod(PythonTernaryOperators.SET_ITEM, + BuiltinTypes.LIST_TYPE.addTernaryMethod(PythonTernaryOperator.SET_ITEM, PythonLikeList.class.getMethod("setItem", PythonInteger.class, PythonLikeObject.class)); - BuiltinTypes.LIST_TYPE.addTernaryMethod(PythonTernaryOperators.SET_ITEM, + BuiltinTypes.LIST_TYPE.addTernaryMethod(PythonTernaryOperator.SET_ITEM, PythonLikeList.class.getMethod("setSlice", PythonSlice.class, PythonLikeObject.class)); // Other diff --git a/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/collections/PythonLikeSet.java b/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/collections/PythonLikeSet.java index d1ebc073..43d2980b 100644 --- a/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/collections/PythonLikeSet.java +++ b/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/collections/PythonLikeSet.java @@ -8,7 +8,7 @@ import java.util.Set; import java.util.function.Predicate; -import ai.timefold.jpyinterpreter.PythonBinaryOperators; +import ai.timefold.jpyinterpreter.PythonBinaryOperator; import ai.timefold.jpyinterpreter.PythonLikeObject; import ai.timefold.jpyinterpreter.PythonOverloadImplementor; import ai.timefold.jpyinterpreter.PythonUnaryOperator; @@ -48,7 +48,7 @@ private static PythonLikeType registerMethods() throws NoSuchMethodException { BuiltinTypes.SET_TYPE.addUnaryMethod(PythonUnaryOperator.ITERATOR, PythonLikeSet.class.getMethod("getIterator")); // Binary - BuiltinTypes.SET_TYPE.addBinaryMethod(PythonBinaryOperators.CONTAINS, + BuiltinTypes.SET_TYPE.addBinaryMethod(PythonBinaryOperator.CONTAINS, PythonLikeSet.class.getMethod("containsItem", PythonLikeObject.class)); // Other @@ -56,75 +56,75 @@ private static PythonLikeType registerMethods() throws NoSuchMethodException { BuiltinTypes.SET_TYPE.addMethod("isdisjoint", PythonLikeSet.class.getMethod("isDisjoint", PythonLikeFrozenSet.class)); BuiltinTypes.SET_TYPE.addMethod("issubset", PythonLikeSet.class.getMethod("isSubset", PythonLikeSet.class)); - BuiltinTypes.SET_TYPE.addBinaryMethod(PythonBinaryOperators.LESS_THAN_OR_EQUAL, + BuiltinTypes.SET_TYPE.addBinaryMethod(PythonBinaryOperator.LESS_THAN_OR_EQUAL, PythonLikeSet.class.getMethod("isSubset", PythonLikeSet.class)); - BuiltinTypes.SET_TYPE.addBinaryMethod(PythonBinaryOperators.LESS_THAN, + BuiltinTypes.SET_TYPE.addBinaryMethod(PythonBinaryOperator.LESS_THAN, PythonLikeSet.class.getMethod("isStrictSubset", PythonLikeSet.class)); BuiltinTypes.SET_TYPE.addMethod("issubset", PythonLikeSet.class.getMethod("isSubset", PythonLikeFrozenSet.class)); - BuiltinTypes.SET_TYPE.addBinaryMethod(PythonBinaryOperators.LESS_THAN_OR_EQUAL, + BuiltinTypes.SET_TYPE.addBinaryMethod(PythonBinaryOperator.LESS_THAN_OR_EQUAL, PythonLikeSet.class.getMethod("isSubset", PythonLikeFrozenSet.class)); - BuiltinTypes.SET_TYPE.addBinaryMethod(PythonBinaryOperators.LESS_THAN, + BuiltinTypes.SET_TYPE.addBinaryMethod(PythonBinaryOperator.LESS_THAN, PythonLikeSet.class.getMethod("isStrictSubset", PythonLikeFrozenSet.class)); BuiltinTypes.SET_TYPE.addMethod("issuperset", PythonLikeSet.class.getMethod("isSuperset", PythonLikeSet.class)); - BuiltinTypes.SET_TYPE.addBinaryMethod(PythonBinaryOperators.GREATER_THAN_OR_EQUAL, + BuiltinTypes.SET_TYPE.addBinaryMethod(PythonBinaryOperator.GREATER_THAN_OR_EQUAL, PythonLikeSet.class.getMethod("isSuperset", PythonLikeSet.class)); - BuiltinTypes.SET_TYPE.addBinaryMethod(PythonBinaryOperators.GREATER_THAN, + BuiltinTypes.SET_TYPE.addBinaryMethod(PythonBinaryOperator.GREATER_THAN, PythonLikeSet.class.getMethod("isStrictSuperset", PythonLikeSet.class)); BuiltinTypes.SET_TYPE.addMethod("issuperset", PythonLikeSet.class.getMethod("isSuperset", PythonLikeFrozenSet.class)); - BuiltinTypes.SET_TYPE.addBinaryMethod(PythonBinaryOperators.GREATER_THAN_OR_EQUAL, + BuiltinTypes.SET_TYPE.addBinaryMethod(PythonBinaryOperator.GREATER_THAN_OR_EQUAL, PythonLikeSet.class.getMethod("isSuperset", PythonLikeFrozenSet.class)); - BuiltinTypes.SET_TYPE.addBinaryMethod(PythonBinaryOperators.GREATER_THAN, + BuiltinTypes.SET_TYPE.addBinaryMethod(PythonBinaryOperator.GREATER_THAN, PythonLikeSet.class.getMethod("isStrictSuperset", PythonLikeFrozenSet.class)); BuiltinTypes.SET_TYPE.addMethod("union", PythonLikeSet.class.getMethod("union", PythonLikeSet.class)); BuiltinTypes.SET_TYPE.addMethod("union", PythonLikeSet.class.getMethod("union", PythonLikeFrozenSet.class)); - BuiltinTypes.SET_TYPE.addBinaryMethod(PythonBinaryOperators.OR, + BuiltinTypes.SET_TYPE.addBinaryMethod(PythonBinaryOperator.OR, PythonLikeSet.class.getMethod("union", PythonLikeSet.class)); - BuiltinTypes.SET_TYPE.addBinaryMethod(PythonBinaryOperators.OR, + BuiltinTypes.SET_TYPE.addBinaryMethod(PythonBinaryOperator.OR, PythonLikeSet.class.getMethod("union", PythonLikeFrozenSet.class)); BuiltinTypes.SET_TYPE.addMethod("intersection", PythonLikeSet.class.getMethod("intersection", PythonLikeSet.class)); BuiltinTypes.SET_TYPE.addMethod("intersection", PythonLikeSet.class.getMethod("intersection", PythonLikeFrozenSet.class)); - BuiltinTypes.SET_TYPE.addBinaryMethod(PythonBinaryOperators.AND, + BuiltinTypes.SET_TYPE.addBinaryMethod(PythonBinaryOperator.AND, PythonLikeSet.class.getMethod("intersection", PythonLikeSet.class)); - BuiltinTypes.SET_TYPE.addBinaryMethod(PythonBinaryOperators.AND, + BuiltinTypes.SET_TYPE.addBinaryMethod(PythonBinaryOperator.AND, PythonLikeSet.class.getMethod("intersection", PythonLikeFrozenSet.class)); BuiltinTypes.SET_TYPE.addMethod("difference", PythonLikeSet.class.getMethod("difference", PythonLikeSet.class)); BuiltinTypes.SET_TYPE.addMethod("difference", PythonLikeSet.class.getMethod("difference", PythonLikeFrozenSet.class)); - BuiltinTypes.SET_TYPE.addBinaryMethod(PythonBinaryOperators.SUBTRACT, + BuiltinTypes.SET_TYPE.addBinaryMethod(PythonBinaryOperator.SUBTRACT, PythonLikeSet.class.getMethod("difference", PythonLikeSet.class)); - BuiltinTypes.SET_TYPE.addBinaryMethod(PythonBinaryOperators.SUBTRACT, + BuiltinTypes.SET_TYPE.addBinaryMethod(PythonBinaryOperator.SUBTRACT, PythonLikeSet.class.getMethod("difference", PythonLikeFrozenSet.class)); BuiltinTypes.SET_TYPE.addMethod("symmetric_difference", PythonLikeSet.class.getMethod("symmetricDifference", PythonLikeSet.class)); BuiltinTypes.SET_TYPE.addMethod("symmetric_difference", PythonLikeSet.class.getMethod("symmetricDifference", PythonLikeFrozenSet.class)); - BuiltinTypes.SET_TYPE.addBinaryMethod(PythonBinaryOperators.XOR, + BuiltinTypes.SET_TYPE.addBinaryMethod(PythonBinaryOperator.XOR, PythonLikeSet.class.getMethod("symmetricDifference", PythonLikeSet.class)); - BuiltinTypes.SET_TYPE.addBinaryMethod(PythonBinaryOperators.XOR, + BuiltinTypes.SET_TYPE.addBinaryMethod(PythonBinaryOperator.XOR, PythonLikeSet.class.getMethod("symmetricDifference", PythonLikeFrozenSet.class)); BuiltinTypes.SET_TYPE.addMethod("update", PythonLikeSet.class.getMethod("update", PythonLikeObject.class)); - BuiltinTypes.SET_TYPE.addBinaryMethod(PythonBinaryOperators.INPLACE_OR, + BuiltinTypes.SET_TYPE.addBinaryMethod(PythonBinaryOperator.INPLACE_OR, PythonLikeSet.class.getMethod("updateWithResult", PythonLikeObject.class)); BuiltinTypes.SET_TYPE.addMethod("intersection_update", PythonLikeSet.class.getMethod("intersectionUpdate", PythonLikeObject.class)); - BuiltinTypes.SET_TYPE.addBinaryMethod(PythonBinaryOperators.INPLACE_AND, + BuiltinTypes.SET_TYPE.addBinaryMethod(PythonBinaryOperator.INPLACE_AND, PythonLikeSet.class.getMethod("intersectionUpdateWithResult", PythonLikeObject.class)); BuiltinTypes.SET_TYPE.addMethod("difference_update", PythonLikeSet.class.getMethod("differenceUpdate", PythonLikeObject.class)); - BuiltinTypes.SET_TYPE.addBinaryMethod(PythonBinaryOperators.INPLACE_SUBTRACT, + BuiltinTypes.SET_TYPE.addBinaryMethod(PythonBinaryOperator.INPLACE_SUBTRACT, PythonLikeSet.class.getMethod("differenceUpdateWithResult", PythonLikeObject.class)); BuiltinTypes.SET_TYPE.addMethod("symmetric_difference_update", PythonLikeSet.class.getMethod("symmetricDifferenceUpdate", PythonLikeObject.class)); - BuiltinTypes.SET_TYPE.addBinaryMethod(PythonBinaryOperators.INPLACE_XOR, + BuiltinTypes.SET_TYPE.addBinaryMethod(PythonBinaryOperator.INPLACE_XOR, PythonLikeSet.class.getMethod("symmetricDifferenceUpdateWithResult", PythonLikeObject.class)); BuiltinTypes.SET_TYPE.addMethod("add", PythonLikeSet.class.getMethod("addItem", PythonLikeObject.class)); diff --git a/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/collections/PythonLikeTuple.java b/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/collections/PythonLikeTuple.java index 7b2bb9da..963ffb3c 100644 --- a/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/collections/PythonLikeTuple.java +++ b/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/collections/PythonLikeTuple.java @@ -9,7 +9,7 @@ import java.util.Objects; import java.util.RandomAccess; -import ai.timefold.jpyinterpreter.PythonBinaryOperators; +import ai.timefold.jpyinterpreter.PythonBinaryOperator; import ai.timefold.jpyinterpreter.PythonLikeObject; import ai.timefold.jpyinterpreter.PythonOverloadImplementor; import ai.timefold.jpyinterpreter.PythonUnaryOperator; @@ -58,15 +58,15 @@ private static PythonLikeType registerMethods() throws NoSuchMethodException { BuiltinTypes.TUPLE_TYPE.addUnaryMethod(PythonUnaryOperator.ITERATOR, PythonLikeTuple.class.getMethod("getIterator")); // Binary - BuiltinTypes.TUPLE_TYPE.addBinaryMethod(PythonBinaryOperators.ADD, + BuiltinTypes.TUPLE_TYPE.addBinaryMethod(PythonBinaryOperator.ADD, PythonLikeTuple.class.getMethod("concatToNew", PythonLikeTuple.class)); - BuiltinTypes.TUPLE_TYPE.addBinaryMethod(PythonBinaryOperators.MULTIPLY, + BuiltinTypes.TUPLE_TYPE.addBinaryMethod(PythonBinaryOperator.MULTIPLY, PythonLikeTuple.class.getMethod("multiplyToNew", PythonInteger.class)); - BuiltinTypes.TUPLE_TYPE.addBinaryMethod(PythonBinaryOperators.GET_ITEM, + BuiltinTypes.TUPLE_TYPE.addBinaryMethod(PythonBinaryOperator.GET_ITEM, PythonLikeTuple.class.getMethod("getItem", PythonInteger.class)); - BuiltinTypes.TUPLE_TYPE.addBinaryMethod(PythonBinaryOperators.GET_ITEM, + BuiltinTypes.TUPLE_TYPE.addBinaryMethod(PythonBinaryOperator.GET_ITEM, PythonLikeTuple.class.getMethod("getSlice", PythonSlice.class)); - BuiltinTypes.TUPLE_TYPE.addBinaryMethod(PythonBinaryOperators.CONTAINS, + BuiltinTypes.TUPLE_TYPE.addBinaryMethod(PythonBinaryOperator.CONTAINS, PythonLikeTuple.class.getMethod("containsItem", PythonLikeObject.class)); // Other diff --git a/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/collections/view/DictItemView.java b/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/collections/view/DictItemView.java index 7aafc91b..78e4277a 100644 --- a/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/collections/view/DictItemView.java +++ b/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/collections/view/DictItemView.java @@ -8,7 +8,7 @@ import java.util.Set; import java.util.function.Predicate; -import ai.timefold.jpyinterpreter.PythonBinaryOperators; +import ai.timefold.jpyinterpreter.PythonBinaryOperator; import ai.timefold.jpyinterpreter.PythonLikeObject; import ai.timefold.jpyinterpreter.PythonOverloadImplementor; import ai.timefold.jpyinterpreter.PythonUnaryOperator; @@ -49,27 +49,27 @@ private static PythonLikeType registerMethods() throws NoSuchMethodException { DictItemView.class.getMethod("toRepresentation")); // Binary - BuiltinTypes.DICT_ITEM_VIEW_TYPE.addBinaryMethod(PythonBinaryOperators.CONTAINS, + BuiltinTypes.DICT_ITEM_VIEW_TYPE.addBinaryMethod(PythonBinaryOperator.CONTAINS, DictItemView.class.getMethod("containsItem", PythonLikeObject.class)); // Set methods BuiltinTypes.DICT_ITEM_VIEW_TYPE.addMethod("isdisjoint", DictItemView.class.getMethod("isDisjoint", DictItemView.class)); - BuiltinTypes.DICT_ITEM_VIEW_TYPE.addBinaryMethod(PythonBinaryOperators.LESS_THAN_OR_EQUAL, + BuiltinTypes.DICT_ITEM_VIEW_TYPE.addBinaryMethod(PythonBinaryOperator.LESS_THAN_OR_EQUAL, DictItemView.class.getMethod("isSubset", DictItemView.class)); - BuiltinTypes.DICT_ITEM_VIEW_TYPE.addBinaryMethod(PythonBinaryOperators.LESS_THAN, + BuiltinTypes.DICT_ITEM_VIEW_TYPE.addBinaryMethod(PythonBinaryOperator.LESS_THAN, DictItemView.class.getMethod("isStrictSubset", DictItemView.class)); - BuiltinTypes.DICT_ITEM_VIEW_TYPE.addBinaryMethod(PythonBinaryOperators.GREATER_THAN_OR_EQUAL, + BuiltinTypes.DICT_ITEM_VIEW_TYPE.addBinaryMethod(PythonBinaryOperator.GREATER_THAN_OR_EQUAL, DictItemView.class.getMethod("isSuperset", DictItemView.class)); - BuiltinTypes.DICT_ITEM_VIEW_TYPE.addBinaryMethod(PythonBinaryOperators.GREATER_THAN, + BuiltinTypes.DICT_ITEM_VIEW_TYPE.addBinaryMethod(PythonBinaryOperator.GREATER_THAN, DictItemView.class.getMethod("isStrictSuperset", DictItemView.class)); - BuiltinTypes.DICT_ITEM_VIEW_TYPE.addBinaryMethod(PythonBinaryOperators.OR, + BuiltinTypes.DICT_ITEM_VIEW_TYPE.addBinaryMethod(PythonBinaryOperator.OR, DictItemView.class.getMethod("union", DictItemView.class)); - BuiltinTypes.DICT_ITEM_VIEW_TYPE.addBinaryMethod(PythonBinaryOperators.AND, + BuiltinTypes.DICT_ITEM_VIEW_TYPE.addBinaryMethod(PythonBinaryOperator.AND, DictItemView.class.getMethod("intersection", DictItemView.class)); - BuiltinTypes.DICT_ITEM_VIEW_TYPE.addBinaryMethod(PythonBinaryOperators.SUBTRACT, + BuiltinTypes.DICT_ITEM_VIEW_TYPE.addBinaryMethod(PythonBinaryOperator.SUBTRACT, DictItemView.class.getMethod("difference", DictItemView.class)); - BuiltinTypes.DICT_ITEM_VIEW_TYPE.addBinaryMethod(PythonBinaryOperators.XOR, + BuiltinTypes.DICT_ITEM_VIEW_TYPE.addBinaryMethod(PythonBinaryOperator.XOR, DictItemView.class.getMethod("symmetricDifference", DictItemView.class)); return BuiltinTypes.DICT_ITEM_VIEW_TYPE; diff --git a/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/collections/view/DictKeyView.java b/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/collections/view/DictKeyView.java index 5afdb9f0..70cd4d15 100644 --- a/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/collections/view/DictKeyView.java +++ b/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/collections/view/DictKeyView.java @@ -4,7 +4,7 @@ import java.util.Set; import java.util.function.Predicate; -import ai.timefold.jpyinterpreter.PythonBinaryOperators; +import ai.timefold.jpyinterpreter.PythonBinaryOperator; import ai.timefold.jpyinterpreter.PythonLikeObject; import ai.timefold.jpyinterpreter.PythonOverloadImplementor; import ai.timefold.jpyinterpreter.PythonUnaryOperator; @@ -42,26 +42,26 @@ private static PythonLikeType registerMethods() throws NoSuchMethodException { DictKeyView.class.getMethod("toRepresentation")); // Binary - BuiltinTypes.DICT_KEY_VIEW_TYPE.addBinaryMethod(PythonBinaryOperators.CONTAINS, + BuiltinTypes.DICT_KEY_VIEW_TYPE.addBinaryMethod(PythonBinaryOperator.CONTAINS, DictKeyView.class.getMethod("containsKey", PythonLikeObject.class)); // Set methods BuiltinTypes.DICT_KEY_VIEW_TYPE.addMethod("isdisjoint", DictKeyView.class.getMethod("isDisjoint", DictKeyView.class)); - BuiltinTypes.DICT_KEY_VIEW_TYPE.addBinaryMethod(PythonBinaryOperators.LESS_THAN_OR_EQUAL, + BuiltinTypes.DICT_KEY_VIEW_TYPE.addBinaryMethod(PythonBinaryOperator.LESS_THAN_OR_EQUAL, DictKeyView.class.getMethod("isSubset", DictKeyView.class)); - BuiltinTypes.DICT_KEY_VIEW_TYPE.addBinaryMethod(PythonBinaryOperators.LESS_THAN, + BuiltinTypes.DICT_KEY_VIEW_TYPE.addBinaryMethod(PythonBinaryOperator.LESS_THAN, DictKeyView.class.getMethod("isStrictSubset", DictKeyView.class)); - BuiltinTypes.DICT_KEY_VIEW_TYPE.addBinaryMethod(PythonBinaryOperators.GREATER_THAN_OR_EQUAL, + BuiltinTypes.DICT_KEY_VIEW_TYPE.addBinaryMethod(PythonBinaryOperator.GREATER_THAN_OR_EQUAL, DictKeyView.class.getMethod("isSuperset", DictKeyView.class)); - BuiltinTypes.DICT_KEY_VIEW_TYPE.addBinaryMethod(PythonBinaryOperators.GREATER_THAN, + BuiltinTypes.DICT_KEY_VIEW_TYPE.addBinaryMethod(PythonBinaryOperator.GREATER_THAN, DictKeyView.class.getMethod("isStrictSuperset", DictKeyView.class)); - BuiltinTypes.DICT_KEY_VIEW_TYPE.addBinaryMethod(PythonBinaryOperators.OR, + BuiltinTypes.DICT_KEY_VIEW_TYPE.addBinaryMethod(PythonBinaryOperator.OR, DictKeyView.class.getMethod("union", DictKeyView.class)); - BuiltinTypes.DICT_KEY_VIEW_TYPE.addBinaryMethod(PythonBinaryOperators.AND, + BuiltinTypes.DICT_KEY_VIEW_TYPE.addBinaryMethod(PythonBinaryOperator.AND, DictKeyView.class.getMethod("intersection", DictKeyView.class)); - BuiltinTypes.DICT_KEY_VIEW_TYPE.addBinaryMethod(PythonBinaryOperators.SUBTRACT, + BuiltinTypes.DICT_KEY_VIEW_TYPE.addBinaryMethod(PythonBinaryOperator.SUBTRACT, DictKeyView.class.getMethod("difference", DictKeyView.class)); - BuiltinTypes.DICT_KEY_VIEW_TYPE.addBinaryMethod(PythonBinaryOperators.XOR, + BuiltinTypes.DICT_KEY_VIEW_TYPE.addBinaryMethod(PythonBinaryOperator.XOR, DictKeyView.class.getMethod("symmetricDifference", DictKeyView.class)); return BuiltinTypes.DICT_KEY_VIEW_TYPE; diff --git a/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/collections/view/DictValueView.java b/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/collections/view/DictValueView.java index 41d0762d..f0f131d6 100644 --- a/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/collections/view/DictValueView.java +++ b/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/collections/view/DictValueView.java @@ -2,7 +2,7 @@ import java.util.Collection; -import ai.timefold.jpyinterpreter.PythonBinaryOperators; +import ai.timefold.jpyinterpreter.PythonBinaryOperator; import ai.timefold.jpyinterpreter.PythonLikeObject; import ai.timefold.jpyinterpreter.PythonOverloadImplementor; import ai.timefold.jpyinterpreter.PythonUnaryOperator; @@ -41,7 +41,7 @@ private static PythonLikeType registerMethods() throws NoSuchMethodException { DictValueView.class.getMethod("toRepresentation")); // Binary - BuiltinTypes.DICT_VALUE_VIEW_TYPE.addBinaryMethod(PythonBinaryOperators.CONTAINS, + BuiltinTypes.DICT_VALUE_VIEW_TYPE.addBinaryMethod(PythonBinaryOperator.CONTAINS, DictValueView.class.getMethod("containsValue", PythonLikeObject.class)); return BuiltinTypes.DICT_VALUE_VIEW_TYPE; diff --git a/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/datetime/PythonDate.java b/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/datetime/PythonDate.java index 094a86b5..e9c84d79 100644 --- a/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/datetime/PythonDate.java +++ b/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/datetime/PythonDate.java @@ -13,7 +13,7 @@ import java.time.temporal.IsoFields; import java.util.List; -import ai.timefold.jpyinterpreter.PythonBinaryOperators; +import ai.timefold.jpyinterpreter.PythonBinaryOperator; import ai.timefold.jpyinterpreter.PythonLikeObject; import ai.timefold.jpyinterpreter.PythonOverloadImplementor; import ai.timefold.jpyinterpreter.PythonUnaryOperator; @@ -72,11 +72,11 @@ private static void registerMethods() throws NoSuchMethodException { PythonDate.class.getMethod("toPythonString")); // Binary Operators - DATE_TYPE.addBinaryMethod(PythonBinaryOperators.ADD, + DATE_TYPE.addBinaryMethod(PythonBinaryOperator.ADD, PythonDate.class.getMethod("add_time_delta", PythonTimeDelta.class)); - DATE_TYPE.addBinaryMethod(PythonBinaryOperators.SUBTRACT, + DATE_TYPE.addBinaryMethod(PythonBinaryOperator.SUBTRACT, PythonDate.class.getMethod("subtract_time_delta", PythonTimeDelta.class)); - DATE_TYPE.addBinaryMethod(PythonBinaryOperators.SUBTRACT, + DATE_TYPE.addBinaryMethod(PythonBinaryOperator.SUBTRACT, PythonDate.class.getMethod("subtract_date", PythonDate.class)); // Methods diff --git a/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/datetime/PythonDateTime.java b/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/datetime/PythonDateTime.java index 783ad245..500476a3 100644 --- a/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/datetime/PythonDateTime.java +++ b/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/datetime/PythonDateTime.java @@ -17,7 +17,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; -import ai.timefold.jpyinterpreter.PythonBinaryOperators; +import ai.timefold.jpyinterpreter.PythonBinaryOperator; import ai.timefold.jpyinterpreter.PythonLikeObject; import ai.timefold.jpyinterpreter.PythonOverloadImplementor; import ai.timefold.jpyinterpreter.PythonUnaryOperator; @@ -143,11 +143,11 @@ private static void registerMethods() throws NoSuchMethodException { PythonDateTime.class.getMethod("toPythonString")); // Binary Operators - DATE_TIME_TYPE.addBinaryMethod(PythonBinaryOperators.ADD, + DATE_TIME_TYPE.addBinaryMethod(PythonBinaryOperator.ADD, PythonDateTime.class.getMethod("add_time_delta", PythonTimeDelta.class)); - DATE_TIME_TYPE.addBinaryMethod(PythonBinaryOperators.SUBTRACT, + DATE_TIME_TYPE.addBinaryMethod(PythonBinaryOperator.SUBTRACT, PythonDateTime.class.getMethod("subtract_time_delta", PythonTimeDelta.class)); - DATE_TIME_TYPE.addBinaryMethod(PythonBinaryOperators.SUBTRACT, + DATE_TIME_TYPE.addBinaryMethod(PythonBinaryOperator.SUBTRACT, PythonDateTime.class.getMethod("subtract_date_time", PythonDateTime.class)); // Instance methods diff --git a/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/datetime/PythonTimeDelta.java b/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/datetime/PythonTimeDelta.java index dc9e585a..c0138915 100644 --- a/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/datetime/PythonTimeDelta.java +++ b/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/datetime/PythonTimeDelta.java @@ -5,7 +5,7 @@ import java.time.temporal.ChronoUnit; import java.time.temporal.TemporalUnit; -import ai.timefold.jpyinterpreter.PythonBinaryOperators; +import ai.timefold.jpyinterpreter.PythonBinaryOperator; import ai.timefold.jpyinterpreter.PythonLikeObject; import ai.timefold.jpyinterpreter.PythonOverloadImplementor; import ai.timefold.jpyinterpreter.PythonUnaryOperator; @@ -77,29 +77,29 @@ private static void registerMethods() throws NoSuchMethodException { PythonTimeDelta.class.getMethod("toPythonRepr")); // Binary - TIME_DELTA_TYPE.addBinaryMethod(PythonBinaryOperators.ADD, + TIME_DELTA_TYPE.addBinaryMethod(PythonBinaryOperator.ADD, PythonTimeDelta.class.getMethod("add_time_delta", PythonTimeDelta.class)); - TIME_DELTA_TYPE.addBinaryMethod(PythonBinaryOperators.SUBTRACT, + TIME_DELTA_TYPE.addBinaryMethod(PythonBinaryOperator.SUBTRACT, PythonTimeDelta.class.getMethod("subtract_time_delta", PythonTimeDelta.class)); - TIME_DELTA_TYPE.addBinaryMethod(PythonBinaryOperators.MULTIPLY, + TIME_DELTA_TYPE.addBinaryMethod(PythonBinaryOperator.MULTIPLY, PythonTimeDelta.class.getMethod("get_integer_multiple", PythonInteger.class)); - TIME_DELTA_TYPE.addBinaryMethod(PythonBinaryOperators.MULTIPLY, + TIME_DELTA_TYPE.addBinaryMethod(PythonBinaryOperator.MULTIPLY, PythonTimeDelta.class.getMethod("get_float_multiple", PythonFloat.class)); - TIME_DELTA_TYPE.addBinaryMethod(PythonBinaryOperators.TRUE_DIVIDE, + TIME_DELTA_TYPE.addBinaryMethod(PythonBinaryOperator.TRUE_DIVIDE, PythonTimeDelta.class.getMethod("divide_time_delta", PythonTimeDelta.class)); - TIME_DELTA_TYPE.addBinaryMethod(PythonBinaryOperators.TRUE_DIVIDE, + TIME_DELTA_TYPE.addBinaryMethod(PythonBinaryOperator.TRUE_DIVIDE, PythonTimeDelta.class.getMethod("divide_integer", PythonInteger.class)); - TIME_DELTA_TYPE.addBinaryMethod(PythonBinaryOperators.TRUE_DIVIDE, + TIME_DELTA_TYPE.addBinaryMethod(PythonBinaryOperator.TRUE_DIVIDE, PythonTimeDelta.class.getMethod("divide_float", PythonFloat.class)); - TIME_DELTA_TYPE.addBinaryMethod(PythonBinaryOperators.FLOOR_DIVIDE, + TIME_DELTA_TYPE.addBinaryMethod(PythonBinaryOperator.FLOOR_DIVIDE, PythonTimeDelta.class.getMethod("floor_divide_time_delta", PythonTimeDelta.class)); - TIME_DELTA_TYPE.addBinaryMethod(PythonBinaryOperators.FLOOR_DIVIDE, + TIME_DELTA_TYPE.addBinaryMethod(PythonBinaryOperator.FLOOR_DIVIDE, PythonTimeDelta.class.getMethod("floor_divide_integer", PythonInteger.class)); - TIME_DELTA_TYPE.addBinaryMethod(PythonBinaryOperators.MODULO, + TIME_DELTA_TYPE.addBinaryMethod(PythonBinaryOperator.MODULO, PythonTimeDelta.class.getMethod("remainder_time_delta", PythonTimeDelta.class)); // Methods diff --git a/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/errors/PythonBaseException.java b/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/errors/PythonBaseException.java index 8df68d70..56e08786 100644 --- a/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/errors/PythonBaseException.java +++ b/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/errors/PythonBaseException.java @@ -9,6 +9,7 @@ import ai.timefold.jpyinterpreter.types.PythonNone; import ai.timefold.jpyinterpreter.types.PythonString; import ai.timefold.jpyinterpreter.types.collections.PythonLikeTuple; +import ai.timefold.jpyinterpreter.types.wrappers.JavaObjectWrapper; /** * Python base class for all exceptions. Equivalent to Java's {@link Throwable}. @@ -79,7 +80,11 @@ public Throwable fillInStackTrace() { @Override public Throwable initCause(Throwable cause) { super.initCause(cause); - __setAttribute("__cause__", (PythonLikeObject) cause); + if (cause instanceof PythonLikeObject pythonError) { + __setAttribute("__cause__", pythonError); + } else { + __setAttribute("__cause__", new JavaObjectWrapper(cause)); + } return this; } diff --git a/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/numeric/PythonFloat.java b/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/numeric/PythonFloat.java index b40329ec..7980da01 100644 --- a/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/numeric/PythonFloat.java +++ b/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/numeric/PythonFloat.java @@ -9,7 +9,7 @@ import java.util.List; import java.util.Map; -import ai.timefold.jpyinterpreter.PythonBinaryOperators; +import ai.timefold.jpyinterpreter.PythonBinaryOperator; import ai.timefold.jpyinterpreter.PythonLikeObject; import ai.timefold.jpyinterpreter.PythonOverloadImplementor; import ai.timefold.jpyinterpreter.PythonUnaryOperator; @@ -71,99 +71,99 @@ private static PythonLikeType registerMethods() throws NoSuchMethodException { BuiltinTypes.FLOAT_TYPE.addUnaryMethod(PythonUnaryOperator.HASH, PythonFloat.class.getMethod("$method$__hash__")); // Binary - BuiltinTypes.FLOAT_TYPE.addLeftBinaryMethod(PythonBinaryOperators.ADD, + BuiltinTypes.FLOAT_TYPE.addLeftBinaryMethod(PythonBinaryOperator.ADD, PythonFloat.class.getMethod("add", PythonLikeObject.class)); - BuiltinTypes.FLOAT_TYPE.addLeftBinaryMethod(PythonBinaryOperators.ADD, + BuiltinTypes.FLOAT_TYPE.addLeftBinaryMethod(PythonBinaryOperator.ADD, PythonFloat.class.getMethod("add", PythonInteger.class)); - BuiltinTypes.FLOAT_TYPE.addLeftBinaryMethod(PythonBinaryOperators.ADD, + BuiltinTypes.FLOAT_TYPE.addLeftBinaryMethod(PythonBinaryOperator.ADD, PythonFloat.class.getMethod("add", PythonFloat.class)); - BuiltinTypes.FLOAT_TYPE.addLeftBinaryMethod(PythonBinaryOperators.SUBTRACT, + BuiltinTypes.FLOAT_TYPE.addLeftBinaryMethod(PythonBinaryOperator.SUBTRACT, PythonFloat.class.getMethod("subtract", PythonLikeObject.class)); - BuiltinTypes.FLOAT_TYPE.addLeftBinaryMethod(PythonBinaryOperators.SUBTRACT, + BuiltinTypes.FLOAT_TYPE.addLeftBinaryMethod(PythonBinaryOperator.SUBTRACT, PythonFloat.class.getMethod("subtract", PythonInteger.class)); - BuiltinTypes.FLOAT_TYPE.addLeftBinaryMethod(PythonBinaryOperators.SUBTRACT, + BuiltinTypes.FLOAT_TYPE.addLeftBinaryMethod(PythonBinaryOperator.SUBTRACT, PythonFloat.class.getMethod("subtract", PythonFloat.class)); - BuiltinTypes.FLOAT_TYPE.addLeftBinaryMethod(PythonBinaryOperators.MULTIPLY, + BuiltinTypes.FLOAT_TYPE.addLeftBinaryMethod(PythonBinaryOperator.MULTIPLY, PythonFloat.class.getMethod("multiply", PythonLikeObject.class)); - BuiltinTypes.FLOAT_TYPE.addLeftBinaryMethod(PythonBinaryOperators.MULTIPLY, + BuiltinTypes.FLOAT_TYPE.addLeftBinaryMethod(PythonBinaryOperator.MULTIPLY, PythonFloat.class.getMethod("multiply", PythonInteger.class)); - BuiltinTypes.FLOAT_TYPE.addLeftBinaryMethod(PythonBinaryOperators.MULTIPLY, + BuiltinTypes.FLOAT_TYPE.addLeftBinaryMethod(PythonBinaryOperator.MULTIPLY, PythonFloat.class.getMethod("multiply", PythonFloat.class)); - BuiltinTypes.FLOAT_TYPE.addLeftBinaryMethod(PythonBinaryOperators.TRUE_DIVIDE, + BuiltinTypes.FLOAT_TYPE.addLeftBinaryMethod(PythonBinaryOperator.TRUE_DIVIDE, PythonFloat.class.getMethod("trueDivide", PythonLikeObject.class)); - BuiltinTypes.FLOAT_TYPE.addLeftBinaryMethod(PythonBinaryOperators.TRUE_DIVIDE, + BuiltinTypes.FLOAT_TYPE.addLeftBinaryMethod(PythonBinaryOperator.TRUE_DIVIDE, PythonFloat.class.getMethod("trueDivide", PythonInteger.class)); - BuiltinTypes.FLOAT_TYPE.addLeftBinaryMethod(PythonBinaryOperators.TRUE_DIVIDE, + BuiltinTypes.FLOAT_TYPE.addLeftBinaryMethod(PythonBinaryOperator.TRUE_DIVIDE, PythonFloat.class.getMethod("trueDivide", PythonFloat.class)); - BuiltinTypes.FLOAT_TYPE.addLeftBinaryMethod(PythonBinaryOperators.FLOOR_DIVIDE, + BuiltinTypes.FLOAT_TYPE.addLeftBinaryMethod(PythonBinaryOperator.FLOOR_DIVIDE, PythonFloat.class.getMethod("floorDivide", PythonLikeObject.class)); - BuiltinTypes.FLOAT_TYPE.addLeftBinaryMethod(PythonBinaryOperators.FLOOR_DIVIDE, + BuiltinTypes.FLOAT_TYPE.addLeftBinaryMethod(PythonBinaryOperator.FLOOR_DIVIDE, PythonFloat.class.getMethod("floorDivide", PythonInteger.class)); - BuiltinTypes.FLOAT_TYPE.addLeftBinaryMethod(PythonBinaryOperators.FLOOR_DIVIDE, + BuiltinTypes.FLOAT_TYPE.addLeftBinaryMethod(PythonBinaryOperator.FLOOR_DIVIDE, PythonFloat.class.getMethod("floorDivide", PythonFloat.class)); - BuiltinTypes.FLOAT_TYPE.addLeftBinaryMethod(PythonBinaryOperators.DIVMOD, + BuiltinTypes.FLOAT_TYPE.addLeftBinaryMethod(PythonBinaryOperator.DIVMOD, PythonFloat.class.getMethod("divmod", PythonLikeObject.class)); - BuiltinTypes.FLOAT_TYPE.addLeftBinaryMethod(PythonBinaryOperators.DIVMOD, + BuiltinTypes.FLOAT_TYPE.addLeftBinaryMethod(PythonBinaryOperator.DIVMOD, PythonFloat.class.getMethod("divmod", PythonInteger.class)); - BuiltinTypes.FLOAT_TYPE.addLeftBinaryMethod(PythonBinaryOperators.DIVMOD, + BuiltinTypes.FLOAT_TYPE.addLeftBinaryMethod(PythonBinaryOperator.DIVMOD, PythonFloat.class.getMethod("divmod", PythonFloat.class)); - BuiltinTypes.FLOAT_TYPE.addLeftBinaryMethod(PythonBinaryOperators.MODULO, + BuiltinTypes.FLOAT_TYPE.addLeftBinaryMethod(PythonBinaryOperator.MODULO, PythonFloat.class.getMethod("modulo", PythonLikeObject.class)); - BuiltinTypes.FLOAT_TYPE.addLeftBinaryMethod(PythonBinaryOperators.MODULO, + BuiltinTypes.FLOAT_TYPE.addLeftBinaryMethod(PythonBinaryOperator.MODULO, PythonFloat.class.getMethod("modulo", PythonInteger.class)); - BuiltinTypes.FLOAT_TYPE.addLeftBinaryMethod(PythonBinaryOperators.MODULO, + BuiltinTypes.FLOAT_TYPE.addLeftBinaryMethod(PythonBinaryOperator.MODULO, PythonFloat.class.getMethod("modulo", PythonFloat.class)); - BuiltinTypes.FLOAT_TYPE.addLeftBinaryMethod(PythonBinaryOperators.POWER, + BuiltinTypes.FLOAT_TYPE.addLeftBinaryMethod(PythonBinaryOperator.POWER, PythonFloat.class.getMethod("power", PythonLikeObject.class)); - BuiltinTypes.FLOAT_TYPE.addLeftBinaryMethod(PythonBinaryOperators.POWER, + BuiltinTypes.FLOAT_TYPE.addLeftBinaryMethod(PythonBinaryOperator.POWER, PythonFloat.class.getMethod("power", PythonInteger.class)); - BuiltinTypes.FLOAT_TYPE.addLeftBinaryMethod(PythonBinaryOperators.POWER, + BuiltinTypes.FLOAT_TYPE.addLeftBinaryMethod(PythonBinaryOperator.POWER, PythonFloat.class.getMethod("power", PythonFloat.class)); // Comparisons - BuiltinTypes.FLOAT_TYPE.addLeftBinaryMethod(PythonBinaryOperators.EQUAL, + BuiltinTypes.FLOAT_TYPE.addLeftBinaryMethod(PythonBinaryOperator.EQUAL, PythonFloat.class.getMethod("equal", PythonLikeObject.class)); - BuiltinTypes.FLOAT_TYPE.addLeftBinaryMethod(PythonBinaryOperators.EQUAL, + BuiltinTypes.FLOAT_TYPE.addLeftBinaryMethod(PythonBinaryOperator.EQUAL, PythonFloat.class.getMethod("equal", PythonInteger.class)); - BuiltinTypes.FLOAT_TYPE.addLeftBinaryMethod(PythonBinaryOperators.EQUAL, + BuiltinTypes.FLOAT_TYPE.addLeftBinaryMethod(PythonBinaryOperator.EQUAL, PythonFloat.class.getMethod("equal", PythonFloat.class)); - BuiltinTypes.FLOAT_TYPE.addLeftBinaryMethod(PythonBinaryOperators.NOT_EQUAL, + BuiltinTypes.FLOAT_TYPE.addLeftBinaryMethod(PythonBinaryOperator.NOT_EQUAL, PythonFloat.class.getMethod("notEqual", PythonLikeObject.class)); - BuiltinTypes.FLOAT_TYPE.addLeftBinaryMethod(PythonBinaryOperators.NOT_EQUAL, + BuiltinTypes.FLOAT_TYPE.addLeftBinaryMethod(PythonBinaryOperator.NOT_EQUAL, PythonFloat.class.getMethod("notEqual", PythonInteger.class)); - BuiltinTypes.FLOAT_TYPE.addLeftBinaryMethod(PythonBinaryOperators.NOT_EQUAL, + BuiltinTypes.FLOAT_TYPE.addLeftBinaryMethod(PythonBinaryOperator.NOT_EQUAL, PythonFloat.class.getMethod("notEqual", PythonFloat.class)); - BuiltinTypes.FLOAT_TYPE.addLeftBinaryMethod(PythonBinaryOperators.LESS_THAN, + BuiltinTypes.FLOAT_TYPE.addLeftBinaryMethod(PythonBinaryOperator.LESS_THAN, PythonFloat.class.getMethod("lessThan", PythonLikeObject.class)); - BuiltinTypes.FLOAT_TYPE.addLeftBinaryMethod(PythonBinaryOperators.LESS_THAN, + BuiltinTypes.FLOAT_TYPE.addLeftBinaryMethod(PythonBinaryOperator.LESS_THAN, PythonFloat.class.getMethod("lessThan", PythonInteger.class)); - BuiltinTypes.FLOAT_TYPE.addLeftBinaryMethod(PythonBinaryOperators.LESS_THAN, + BuiltinTypes.FLOAT_TYPE.addLeftBinaryMethod(PythonBinaryOperator.LESS_THAN, PythonFloat.class.getMethod("lessThan", PythonFloat.class)); - BuiltinTypes.FLOAT_TYPE.addLeftBinaryMethod(PythonBinaryOperators.LESS_THAN_OR_EQUAL, + BuiltinTypes.FLOAT_TYPE.addLeftBinaryMethod(PythonBinaryOperator.LESS_THAN_OR_EQUAL, PythonFloat.class.getMethod("lessThanOrEqual", PythonLikeObject.class)); - BuiltinTypes.FLOAT_TYPE.addLeftBinaryMethod(PythonBinaryOperators.LESS_THAN_OR_EQUAL, + BuiltinTypes.FLOAT_TYPE.addLeftBinaryMethod(PythonBinaryOperator.LESS_THAN_OR_EQUAL, PythonFloat.class.getMethod("lessThanOrEqual", PythonInteger.class)); - BuiltinTypes.FLOAT_TYPE.addLeftBinaryMethod(PythonBinaryOperators.LESS_THAN_OR_EQUAL, + BuiltinTypes.FLOAT_TYPE.addLeftBinaryMethod(PythonBinaryOperator.LESS_THAN_OR_EQUAL, PythonFloat.class.getMethod("lessThanOrEqual", PythonFloat.class)); - BuiltinTypes.FLOAT_TYPE.addLeftBinaryMethod(PythonBinaryOperators.GREATER_THAN, + BuiltinTypes.FLOAT_TYPE.addLeftBinaryMethod(PythonBinaryOperator.GREATER_THAN, PythonFloat.class.getMethod("greaterThan", PythonLikeObject.class)); - BuiltinTypes.FLOAT_TYPE.addLeftBinaryMethod(PythonBinaryOperators.GREATER_THAN, + BuiltinTypes.FLOAT_TYPE.addLeftBinaryMethod(PythonBinaryOperator.GREATER_THAN, PythonFloat.class.getMethod("greaterThan", PythonInteger.class)); - BuiltinTypes.FLOAT_TYPE.addLeftBinaryMethod(PythonBinaryOperators.GREATER_THAN, + BuiltinTypes.FLOAT_TYPE.addLeftBinaryMethod(PythonBinaryOperator.GREATER_THAN, PythonFloat.class.getMethod("greaterThan", PythonFloat.class)); - BuiltinTypes.FLOAT_TYPE.addLeftBinaryMethod(PythonBinaryOperators.GREATER_THAN_OR_EQUAL, + BuiltinTypes.FLOAT_TYPE.addLeftBinaryMethod(PythonBinaryOperator.GREATER_THAN_OR_EQUAL, PythonFloat.class.getMethod("greaterThanOrEqual", PythonLikeObject.class)); - BuiltinTypes.FLOAT_TYPE.addLeftBinaryMethod(PythonBinaryOperators.GREATER_THAN_OR_EQUAL, + BuiltinTypes.FLOAT_TYPE.addLeftBinaryMethod(PythonBinaryOperator.GREATER_THAN_OR_EQUAL, PythonFloat.class.getMethod("greaterThanOrEqual", PythonInteger.class)); - BuiltinTypes.FLOAT_TYPE.addLeftBinaryMethod(PythonBinaryOperators.GREATER_THAN_OR_EQUAL, + BuiltinTypes.FLOAT_TYPE.addLeftBinaryMethod(PythonBinaryOperator.GREATER_THAN_OR_EQUAL, PythonFloat.class.getMethod("greaterThanOrEqual", PythonFloat.class)); // Other BuiltinTypes.FLOAT_TYPE.addMethod("__round__", PythonFloat.class.getMethod("round")); BuiltinTypes.FLOAT_TYPE.addMethod("__round__", PythonFloat.class.getMethod("round", PythonInteger.class)); - BuiltinTypes.FLOAT_TYPE.addBinaryMethod(PythonBinaryOperators.FORMAT, + BuiltinTypes.FLOAT_TYPE.addBinaryMethod(PythonBinaryOperator.FORMAT, PythonFloat.class.getMethod("$method$__format__")); - BuiltinTypes.FLOAT_TYPE.addBinaryMethod(PythonBinaryOperators.FORMAT, + BuiltinTypes.FLOAT_TYPE.addBinaryMethod(PythonBinaryOperator.FORMAT, PythonFloat.class.getMethod("$method$__format__", PythonLikeObject.class)); return BuiltinTypes.FLOAT_TYPE; diff --git a/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/numeric/PythonInteger.java b/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/numeric/PythonInteger.java index 196dfa33..942f6ea9 100644 --- a/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/numeric/PythonInteger.java +++ b/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/numeric/PythonInteger.java @@ -7,7 +7,7 @@ import java.util.List; import java.util.Map; -import ai.timefold.jpyinterpreter.PythonBinaryOperators; +import ai.timefold.jpyinterpreter.PythonBinaryOperator; import ai.timefold.jpyinterpreter.PythonLikeObject; import ai.timefold.jpyinterpreter.PythonOverloadImplementor; import ai.timefold.jpyinterpreter.PythonUnaryOperator; @@ -73,138 +73,138 @@ private static PythonLikeType registerMethods() throws NoSuchMethodException { BuiltinTypes.INT_TYPE.addUnaryMethod(PythonUnaryOperator.HASH, PythonInteger.class.getMethod("$method$__hash__")); // Binary - BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperators.ADD, + BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperator.ADD, PythonInteger.class.getMethod("add", PythonLikeObject.class)); - BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperators.ADD, + BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperator.ADD, PythonInteger.class.getMethod("add", PythonInteger.class)); - BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperators.ADD, + BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperator.ADD, PythonInteger.class.getMethod("add", PythonFloat.class)); - BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperators.SUBTRACT, + BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperator.SUBTRACT, PythonInteger.class.getMethod("subtract", PythonLikeObject.class)); - BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperators.SUBTRACT, + BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperator.SUBTRACT, PythonInteger.class.getMethod("subtract", PythonInteger.class)); - BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperators.SUBTRACT, + BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperator.SUBTRACT, PythonInteger.class.getMethod("subtract", PythonFloat.class)); - BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperators.MULTIPLY, + BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperator.MULTIPLY, PythonInteger.class.getMethod("multiply", PythonLikeObject.class)); - BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperators.MULTIPLY, + BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperator.MULTIPLY, PythonInteger.class.getMethod("multiply", PythonInteger.class)); - BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperators.MULTIPLY, + BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperator.MULTIPLY, PythonInteger.class.getMethod("multiply", PythonFloat.class)); - BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperators.TRUE_DIVIDE, + BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperator.TRUE_DIVIDE, PythonInteger.class.getMethod("trueDivide", PythonLikeObject.class)); - BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperators.TRUE_DIVIDE, + BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperator.TRUE_DIVIDE, PythonInteger.class.getMethod("trueDivide", PythonInteger.class)); - BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperators.TRUE_DIVIDE, + BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperator.TRUE_DIVIDE, PythonInteger.class.getMethod("trueDivide", PythonFloat.class)); - BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperators.FLOOR_DIVIDE, + BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperator.FLOOR_DIVIDE, PythonInteger.class.getMethod("floorDivide", PythonLikeObject.class)); - BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperators.FLOOR_DIVIDE, + BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperator.FLOOR_DIVIDE, PythonInteger.class.getMethod("floorDivide", PythonInteger.class)); - BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperators.FLOOR_DIVIDE, + BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperator.FLOOR_DIVIDE, PythonInteger.class.getMethod("floorDivide", PythonFloat.class)); - BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperators.DIVMOD, + BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperator.DIVMOD, PythonInteger.class.getMethod("divmod", PythonInteger.class)); - BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperators.DIVMOD, + BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperator.DIVMOD, PythonInteger.class.getMethod("divmod", PythonFloat.class)); - BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperators.MODULO, + BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperator.MODULO, PythonInteger.class.getMethod("modulo", PythonLikeObject.class)); - BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperators.MODULO, + BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperator.MODULO, PythonInteger.class.getMethod("modulo", PythonInteger.class)); - BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperators.MODULO, + BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperator.MODULO, PythonInteger.class.getMethod("modulo", PythonFloat.class)); - BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperators.POWER, + BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperator.POWER, PythonInteger.class.getMethod("power", PythonLikeObject.class)); - BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperators.POWER, + BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperator.POWER, PythonInteger.class.getMethod("power", PythonInteger.class)); - BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperators.POWER, + BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperator.POWER, PythonInteger.class.getMethod("power", PythonFloat.class)); - BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperators.LSHIFT, + BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperator.LSHIFT, PythonInteger.class.getMethod("shiftLeft", PythonLikeObject.class)); - BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperators.LSHIFT, + BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperator.LSHIFT, PythonInteger.class.getMethod("shiftLeft", PythonInteger.class)); - BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperators.RSHIFT, + BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperator.RSHIFT, PythonInteger.class.getMethod("shiftRight", PythonLikeObject.class)); - BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperators.RSHIFT, + BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperator.RSHIFT, PythonInteger.class.getMethod("shiftRight", PythonInteger.class)); - BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperators.AND, + BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperator.AND, PythonInteger.class.getMethod("bitwiseAnd", PythonLikeObject.class)); - BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperators.AND, + BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperator.AND, PythonInteger.class.getMethod("bitwiseAnd", PythonInteger.class)); - BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperators.OR, + BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperator.OR, PythonInteger.class.getMethod("bitwiseOr", PythonLikeObject.class)); - BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperators.OR, + BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperator.OR, PythonInteger.class.getMethod("bitwiseOr", PythonInteger.class)); - BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperators.XOR, + BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperator.XOR, PythonInteger.class.getMethod("bitwiseXor", PythonLikeObject.class)); - BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperators.XOR, + BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperator.XOR, PythonInteger.class.getMethod("bitwiseXor", PythonInteger.class)); // Ternary - BuiltinTypes.INT_TYPE.addBinaryMethod(PythonBinaryOperators.POWER, + BuiltinTypes.INT_TYPE.addBinaryMethod(PythonBinaryOperator.POWER, PythonInteger.class.getMethod("power", PythonInteger.class, PythonInteger.class)); // Comparisons - BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperators.EQUAL, + BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperator.EQUAL, PythonInteger.class.getMethod("equal", PythonLikeObject.class)); - BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperators.EQUAL, + BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperator.EQUAL, PythonInteger.class.getMethod("equal", PythonInteger.class)); - BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperators.EQUAL, + BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperator.EQUAL, PythonInteger.class.getMethod("equal", PythonFloat.class)); - BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperators.NOT_EQUAL, + BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperator.NOT_EQUAL, PythonInteger.class.getMethod("notEqual", PythonLikeObject.class)); - BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperators.NOT_EQUAL, + BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperator.NOT_EQUAL, PythonInteger.class.getMethod("notEqual", PythonInteger.class)); - BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperators.NOT_EQUAL, + BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperator.NOT_EQUAL, PythonInteger.class.getMethod("notEqual", PythonFloat.class)); - BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperators.LESS_THAN, + BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperator.LESS_THAN, PythonInteger.class.getMethod("lessThan", PythonLikeObject.class)); - BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperators.LESS_THAN, + BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperator.LESS_THAN, PythonInteger.class.getMethod("lessThan", PythonInteger.class)); - BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperators.LESS_THAN, + BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperator.LESS_THAN, PythonInteger.class.getMethod("lessThan", PythonFloat.class)); - BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperators.LESS_THAN_OR_EQUAL, + BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperator.LESS_THAN_OR_EQUAL, PythonInteger.class.getMethod("lessThanOrEqual", PythonLikeObject.class)); - BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperators.LESS_THAN_OR_EQUAL, + BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperator.LESS_THAN_OR_EQUAL, PythonInteger.class.getMethod("lessThanOrEqual", PythonInteger.class)); - BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperators.LESS_THAN_OR_EQUAL, + BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperator.LESS_THAN_OR_EQUAL, PythonInteger.class.getMethod("lessThanOrEqual", PythonFloat.class)); - BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperators.GREATER_THAN, + BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperator.GREATER_THAN, PythonInteger.class.getMethod("greaterThan", PythonLikeObject.class)); - BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperators.GREATER_THAN, + BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperator.GREATER_THAN, PythonInteger.class.getMethod("greaterThan", PythonInteger.class)); - BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperators.GREATER_THAN, + BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperator.GREATER_THAN, PythonInteger.class.getMethod("greaterThan", PythonFloat.class)); - BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperators.GREATER_THAN_OR_EQUAL, + BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperator.GREATER_THAN_OR_EQUAL, PythonInteger.class.getMethod("greaterThanOrEqual", PythonLikeObject.class)); - BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperators.GREATER_THAN_OR_EQUAL, + BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperator.GREATER_THAN_OR_EQUAL, PythonInteger.class.getMethod("greaterThanOrEqual", PythonInteger.class)); - BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperators.GREATER_THAN_OR_EQUAL, + BuiltinTypes.INT_TYPE.addLeftBinaryMethod(PythonBinaryOperator.GREATER_THAN_OR_EQUAL, PythonInteger.class.getMethod("greaterThanOrEqual", PythonFloat.class)); // Other BuiltinTypes.INT_TYPE.addMethod("__round__", PythonInteger.class.getMethod("round")); BuiltinTypes.INT_TYPE.addMethod("__round__", PythonInteger.class.getMethod("round", PythonInteger.class)); - BuiltinTypes.INT_TYPE.addBinaryMethod(PythonBinaryOperators.FORMAT, + BuiltinTypes.INT_TYPE.addBinaryMethod(PythonBinaryOperator.FORMAT, PythonInteger.class.getMethod("$method$__format__")); - BuiltinTypes.INT_TYPE.addBinaryMethod(PythonBinaryOperators.FORMAT, + BuiltinTypes.INT_TYPE.addBinaryMethod(PythonBinaryOperator.FORMAT, PythonInteger.class.getMethod("$method$__format__", PythonLikeObject.class)); return BuiltinTypes.INT_TYPE; diff --git a/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/wrappers/JavaMethodReference.java b/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/wrappers/JavaMethodReference.java index dc7eea50..e877ef00 100644 --- a/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/wrappers/JavaMethodReference.java +++ b/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/wrappers/JavaMethodReference.java @@ -20,6 +20,10 @@ public JavaMethodReference(Method method, Map parameterNameToIn this.parameterNameToIndexMap = parameterNameToIndexMap; } + public Method getMethod() { + return method; + } + @Override public PythonLikeObject $call(List positionalArguments, Map namedArguments, PythonLikeObject callerInstance) { diff --git a/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/wrappers/JavaObjectWrapper.java b/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/wrappers/JavaObjectWrapper.java index 95afab1f..9c301c80 100644 --- a/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/wrappers/JavaObjectWrapper.java +++ b/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/wrappers/JavaObjectWrapper.java @@ -5,68 +5,90 @@ import java.lang.reflect.Member; import java.lang.reflect.Method; import java.lang.reflect.Modifier; +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; import java.util.HashMap; +import java.util.HashSet; +import java.util.IdentityHashMap; +import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Queue; +import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; +import ai.timefold.jpyinterpreter.PythonBinaryOperator; import ai.timefold.jpyinterpreter.PythonLikeObject; +import ai.timefold.jpyinterpreter.PythonUnaryOperator; import ai.timefold.jpyinterpreter.implementors.JavaPythonTypeConversionImplementor; import ai.timefold.jpyinterpreter.types.PythonLikeType; +import ai.timefold.jpyinterpreter.types.errors.AttributeError; +import ai.timefold.jpyinterpreter.types.errors.RuntimeError; import ai.timefold.jpyinterpreter.types.numeric.PythonInteger; -public class JavaObjectWrapper implements PythonLikeObject, Comparable { +public class JavaObjectWrapper implements PythonLikeObject, + Iterable, + Comparable { final static Map, PythonLikeType> classToPythonTypeMap = new HashMap<>(); - final static Map, Map>> classToAttributeNameToMemberListMap = new HashMap<>(); + final static Map, Map> classToAttributeNameToMemberListMap = new HashMap<>(); private final PythonLikeType type; private final Object wrappedObject; private final Class objectClass; - private final Map> attributeNameToMemberListMap; + private final Map attributeNameToMemberListMap; + private final Map convertedObjectMap; - private static Stream getDeclaredMembersStream(Class baseClass) { - Stream fieldStream = Stream.of(baseClass.getDeclaredFields()).filter((field) -> !field.isSynthetic()); - Stream methodStream = Stream.of(baseClass.getDeclaredMethods()).filter((method) -> !method.isSynthetic()); - return Stream.concat(fieldStream, methodStream); + private static Map getAllFields(Class baseClass) { + return getAllDeclaredMembers(baseClass) + .stream() + .filter(member -> member instanceof Field && !Modifier.isStatic(member.getModifiers())) + .collect(Collectors.toMap(Member::getName, member -> (Field) member, + (oldMember, newMember) -> { + if (oldMember.getDeclaringClass().isAssignableFrom(newMember.getDeclaringClass())) { + return newMember; + } else { + return oldMember; + } + })); } - public static Map> getAllFields(Class baseClass) { + private static List getAllDeclaredMembers(Class baseClass) { Class clazz = baseClass; - Stream memberStream; - for (memberStream = Stream.empty(); clazz != null; clazz = clazz.getSuperclass()) { - memberStream = Stream.concat(memberStream, getDeclaredMembersStream(clazz)); + List members = new ArrayList<>(); + for (; clazz != null; clazz = clazz.getSuperclass()) { + Stream.of(clazz.getDeclaredFields()) + .filter((field) -> !field.isSynthetic()) + .forEach(members::add); + Stream.of(clazz.getDeclaredMethods()) + .filter((method) -> !method.isSynthetic()) + .forEach(members::add); } - - return memberStream - .filter(member -> member instanceof Field) - .collect(Collectors.groupingBy(Member::getName, - Collectors.mapping( - member -> member, - Collectors.toList()))); + return members; } private Method getGetterMethod(Field field) { - String propertyName = field.getName(); - String capitalizedName = - propertyName.isEmpty() ? "" : propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1); - String getterName = "get" + capitalizedName; - - if (attributeNameToMemberListMap.containsKey(getterName)) { - List candidates = attributeNameToMemberListMap.get(getterName); - for (Member candidate : candidates) { - if (candidate instanceof Method) { - Method method = (Method) candidate; - if (method.getParameterCount() == 0) { - return method; - } - } - } + String getterName; + if (objectClass.isRecord()) { + getterName = field.getName(); + } else { + String propertyName = field.getName(); + String capitalizedName = + propertyName.isEmpty() ? "" : propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1); + getterName = (field.getType().equals(boolean.class) ? "is" : "get") + + capitalizedName; } - throw new IllegalArgumentException("Cannot get attribute '" + field.getName() + "' on type '" + objectClass + "'"); + PythonLikeObject object = type.__getAttributeOrNull(getterName); + if (object instanceof JavaMethodReference methodReference) { + return methodReference.getMethod(); + } + throw new AttributeError("Cannot get attribute (%s) on class (%s)." + .formatted(field.getName(), type)); } private Method getSetterMethod(Field field) { @@ -75,39 +97,135 @@ private Method getSetterMethod(Field field) { propertyName.isEmpty() ? "" : propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1); String setterName = "set" + capitalizedName; - if (attributeNameToMemberListMap.containsKey(setterName)) { - List candidates = attributeNameToMemberListMap.get(setterName); - for (Member candidate : candidates) { - if (candidate instanceof Method) { - Method method = (Method) candidate; - if (method.getParameterCount() == 1 && method.getParameterTypes()[0].equals(field.getType())) { - return method; - } - } - } + PythonLikeObject object = type.__getAttributeOrNull(setterName); + if (object instanceof JavaMethodReference methodReference) { + return methodReference.getMethod(); } - throw new IllegalArgumentException("Cannot get attribute '" + field.getName() + "' on type '" + objectClass + "'"); + throw new AttributeError("Cannot set attribute (%s) on class (%s)." + .formatted(field.getName(), type)); } public JavaObjectWrapper(Object wrappedObject) { + this(wrappedObject, new IdentityHashMap<>()); + } + + public JavaObjectWrapper(Object wrappedObject, Map convertedObjectMap) { + convertedObjectMap.put(wrappedObject, this); this.wrappedObject = wrappedObject; this.objectClass = wrappedObject.getClass(); + this.convertedObjectMap = convertedObjectMap; this.attributeNameToMemberListMap = classToAttributeNameToMemberListMap.computeIfAbsent(objectClass, JavaObjectWrapper::getAllFields); - this.type = getPythonTypeForClass(objectClass); + this.type = getPythonTypeForClass(objectClass, convertedObjectMap); } public static PythonLikeType getPythonTypeForClass(Class objectClass) { - return classToPythonTypeMap.computeIfAbsent(objectClass, JavaObjectWrapper::generatePythonTypeForClass); + return getPythonTypeForClass(objectClass, new IdentityHashMap<>()); } - private static PythonLikeType generatePythonTypeForClass(Class objectClass) { - PythonLikeType out = new PythonLikeType(objectClass.getName(), JavaObjectWrapper.class); - getDeclaredMembersStream(objectClass) - .filter(member -> member instanceof Method) - .forEach(member -> { - out.__dir__.put(member.getName(), new JavaMethodReference((Method) member, Map.of())); - }); + public static PythonLikeType getPythonTypeForClass(Class objectClass, Map convertedObjectMap) { + if (classToPythonTypeMap.containsKey(objectClass)) { + return classToPythonTypeMap.get(objectClass); + } + PythonLikeType out = generatePythonTypeForClass(objectClass, convertedObjectMap); + classToPythonTypeMap.put(objectClass, out); + return out; + } + + private static boolean isInaccessible(Member member) { + return isInaccessible(member.getDeclaringClass()); + } + + private static boolean isInaccessible(Class clazz) { + for (Class declaringClass = clazz; declaringClass != null; declaringClass = declaringClass.getDeclaringClass()) { + if (!Modifier.isPublic(declaringClass.getModifiers())) { + return true; + } + } + return false; + } + + private static Method findMethodInInterfaces(Class declaringClass, Method method) { + Queue> toVisit = new ArrayDeque<>(); + while (declaringClass != null) { + toVisit.addAll(List.of(declaringClass.getInterfaces())); + declaringClass = declaringClass.getSuperclass(); + } + Set> visited = new HashSet<>(); + while (!toVisit.isEmpty()) { + Class interfaceClass = toVisit.poll(); + if (visited.contains(interfaceClass)) { + continue; + } + visited.add(interfaceClass); + toVisit.addAll(Arrays.asList(interfaceClass.getInterfaces())); + if (isInaccessible(interfaceClass)) { + continue; + } + try { + return interfaceClass.getMethod(method.getName(), method.getParameterTypes()); + } catch (NoSuchMethodException e) { + // Intentionally empty, need to search other interfaces + } + } + return null; + } + + private static void addMemberToPythonType(PythonLikeType type, Member member, + Map convertedObjectMap) { + if (member instanceof Method method) { + if (isInaccessible(member)) { + method = findMethodInInterfaces(method.getDeclaringClass(), method); + if (method == null) { + return; + } + } + // For certain Collection/List/Map methods, also add their corresponding dunder methods, + // so a[x], x in a, len(a) will work. + switch (method.getName()) { + case "size" -> { + if (Collection.class.isAssignableFrom(method.getDeclaringClass())) { + type.__dir__.put(PythonUnaryOperator.LENGTH.getDunderMethod(), + new JavaMethodReference(method, Map.of())); + } + } + case "contains" -> { + if (Collection.class.isAssignableFrom(method.getDeclaringClass())) { + type.__dir__.put(PythonBinaryOperator.CONTAINS.getDunderMethod(), + new JavaMethodReference(method, Map.of())); + } + } + case "get" -> { + type.__dir__.put(PythonBinaryOperator.GET_ITEM.getDunderMethod(), + new JavaMethodReference(method, Map.of())); + } + } + type.__dir__.put(method.getName(), new JavaMethodReference(method, Map.of())); + } else { + if (isInaccessible(member)) { + return; + } + Field field = (Field) member; + if (Modifier.isPublic(field.getModifiers())) { + try { + type.__dir__.put(field.getName(), + JavaPythonTypeConversionImplementor.wrapJavaObject(field.get(null), + convertedObjectMap)); + } catch (IllegalAccessException e) { + throw (RuntimeError) new RuntimeError("Cannot get attribute (%s) on type (%s)." + .formatted(field.getName(), type.getTypeName())).initCause(e); + } + } + } + } + + private static PythonLikeType generatePythonTypeForClass(Class objectClass, + Map convertedObjectMap) { + var out = new PythonLikeType(objectClass.getName(), JavaObjectWrapper.class); + getAllDeclaredMembers(objectClass) + .stream() + .filter(member -> Modifier.isStatic(member.getModifiers()) || member instanceof Method) + .forEach(member -> addMemberToPythonType(out, member, convertedObjectMap)); return out; } @@ -117,70 +235,42 @@ public Object getWrappedObject() { @Override public PythonLikeObject __getAttributeOrNull(String attributeName) { - List candidates = attributeNameToMemberListMap.get(attributeName); - if (candidates == null) { + Field field = attributeNameToMemberListMap.get(attributeName); + if (field == null) { return null; } - if (candidates.size() == 1) { - Member candidate = candidates.get(0); - if (candidate instanceof Field) { - Field field = (Field) candidate; - try { - Object result; - if (Modifier.isPublic(field.getModifiers())) { - result = field.get(wrappedObject); - } else { - Method getterMethod = getGetterMethod(field); - result = getterMethod.invoke(wrappedObject); - } - return JavaPythonTypeConversionImplementor.wrapJavaObject(result); - } catch (IllegalAccessException | InvocationTargetException e) { - throw new IllegalArgumentException( - "Cannot modify attribute '" + attributeName + "' on type '" + objectClass + "'"); - } - } else if (candidate instanceof Method) { - Method method = (Method) candidate; - return new JavaMethodReference(method, Map.of()); + Object result; + try { + if (Modifier.isPublic(field.getModifiers())) { + result = field.get(wrappedObject); } else { - throw new IllegalStateException("Unknown member type (" + candidate.getClass() + ")"); + Method getterMethod = getGetterMethod(field); + result = getterMethod.invoke(wrappedObject); } - } else { - // TODO - throw new IllegalStateException("Ambiguous attribute for type '" + objectClass + "': multiple candidates match '" - + attributeName + "': (" + candidates + ")."); + return JavaPythonTypeConversionImplementor.wrapJavaObject(result, convertedObjectMap); + } catch (InvocationTargetException | IllegalAccessException e) { + throw (RuntimeError) new RuntimeError("Cannot get attribute (%s) on object (%s)." + .formatted(attributeName, this)).initCause(e); } } @Override public void __setAttribute(String attributeName, PythonLikeObject value) { - List candidates = attributeNameToMemberListMap.get(attributeName); - if (candidates == null) { - throw new IllegalArgumentException("type '" + objectClass + "' does not have attribute '" + attributeName + "'"); - } - if (candidates.size() == 1) { - Member candidate = candidates.get(0); - if (candidate instanceof Field) { - Field field = (Field) candidate; - Object javaValue = JavaPythonTypeConversionImplementor.convertPythonObjectToJavaType(field.getType(), value); - try { - if (Modifier.isPublic(field.getModifiers())) { - field.set(wrappedObject, javaValue); - } else { - Method setterMethod = getSetterMethod(field); - setterMethod.invoke(wrappedObject, javaValue); - } - } catch (IllegalAccessException | InvocationTargetException e) { - throw new IllegalArgumentException( - "Cannot modify attribute '" + attributeName + "' on type '" + objectClass + "'"); - } - } else if (candidate instanceof Method) { - throw new IllegalArgumentException( - "Cannot modify attribute '" + attributeName + "' on type '" + objectClass + "'"); + Field field = attributeNameToMemberListMap.get(attributeName); + if (field == null) { + throw new AttributeError("(%s) object does not have attribute (%s)." + .formatted(type, attributeName)); + } + try { + Object javaObject = JavaPythonTypeConversionImplementor.convertPythonObjectToJavaType(field.getType(), value); + if (Modifier.isPublic(field.getModifiers())) { + field.set(wrappedObject, javaObject); } else { - throw new IllegalStateException("Unknown member type (" + candidate.getClass() + ")"); + Method setterMethod = getSetterMethod(field); + setterMethod.invoke(wrappedObject, javaObject); } - } else { - throw new IllegalArgumentException("Cannot modify attribute '" + attributeName + "' on type '" + objectClass + "'"); + } catch (InvocationTargetException | IllegalAccessException e) { + throw new RuntimeException(e); } } @@ -195,21 +285,20 @@ public PythonLikeType __getType() { } @Override + @SuppressWarnings({ "unchecked", "rawtypes" }) public int compareTo(JavaObjectWrapper javaObjectWrapper) { - List compareToMembers = attributeNameToMemberListMap.get("compareTo"); - for (Member member : compareToMembers) { - if (member instanceof Method) { - Method method = (Method) member; - if (method.getDeclaringClass().equals(Comparable.class)) { - try { - return (int) method.invoke(wrappedObject, javaObjectWrapper.wrappedObject); - } catch (IllegalAccessException | InvocationTargetException e) { - throw new IllegalStateException(e); - } - } - } + if (!(wrappedObject instanceof Comparable comparable)) { + throw new IllegalStateException("Class (%s) does not implement (%s).".formatted(objectClass, Comparable.class)); + } + return comparable.compareTo(javaObjectWrapper.wrappedObject); + } + + @Override + public Iterator iterator() { + if (!(wrappedObject instanceof Iterable iterable)) { + throw new IllegalStateException("Class (%s) does not implement (%s).".formatted(objectClass, Iterable.class)); } - throw new IllegalStateException("Class " + objectClass + " does not implement Comparable"); + return new WrappingJavaObjectIterator(iterable.iterator()); } @Override diff --git a/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/wrappers/WrappingJavaObjectIterator.java b/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/wrappers/WrappingJavaObjectIterator.java new file mode 100644 index 00000000..e5c83320 --- /dev/null +++ b/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/wrappers/WrappingJavaObjectIterator.java @@ -0,0 +1,16 @@ +package ai.timefold.jpyinterpreter.types.wrappers; + +import java.util.Iterator; + +record WrappingJavaObjectIterator(Iterator delegate) implements Iterator { + + @Override + public boolean hasNext() { + return delegate.hasNext(); + } + + @Override + public JavaObjectWrapper next() { + return new JavaObjectWrapper(delegate.next()); + } +} diff --git a/jpyinterpreter/src/test/java/ai/timefold/jpyinterpreter/types/wrappers/JavaObjectWrapperTest.java b/jpyinterpreter/src/test/java/ai/timefold/jpyinterpreter/types/wrappers/JavaObjectWrapperTest.java new file mode 100644 index 00000000..22edebbd --- /dev/null +++ b/jpyinterpreter/src/test/java/ai/timefold/jpyinterpreter/types/wrappers/JavaObjectWrapperTest.java @@ -0,0 +1,136 @@ +package ai.timefold.jpyinterpreter.types.wrappers; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.List; +import java.util.Map; + +import ai.timefold.jpyinterpreter.PythonBinaryOperator; +import ai.timefold.jpyinterpreter.PythonLikeObject; +import ai.timefold.jpyinterpreter.PythonUnaryOperator; +import ai.timefold.jpyinterpreter.types.PythonLikeFunction; +import ai.timefold.jpyinterpreter.types.PythonString; +import ai.timefold.jpyinterpreter.types.numeric.PythonBoolean; +import ai.timefold.jpyinterpreter.types.numeric.PythonInteger; +import ai.timefold.jpyinterpreter.types.wrappers.inaccessible.PublicInterface; + +import org.junit.jupiter.api.Test; + +public class JavaObjectWrapperTest { + public record TestObject(String name) { + public String getName() { + return name; + } + } + + @Test + void testEquals() { + assertThat(new TestObject("a")).isEqualTo(new TestObject("a")); + assertThat(new TestObject("a")).isNotEqualTo(new TestObject("b")); + } + + @Test + void testHashCode() { + assertThat(new TestObject("a").hashCode()).isEqualTo(new TestObject("a").hashCode()); + } + + @Test + void testCallingMethod() { + TestObject object = new TestObject("My name"); + JavaObjectWrapper wrapper = new JavaObjectWrapper(object); + PythonLikeObject wrappedFunction = wrapper.$method$__getattribute__(PythonString.valueOf("getName")); + assertThat(wrappedFunction).isInstanceOf(PythonLikeFunction.class); + PythonLikeFunction function = (PythonLikeFunction) wrappedFunction; + assertThat(function.$call(List.of(), Map.of(), null)).isEqualTo(PythonString.valueOf("My name")); + } + + @Test + void testCallingMethodOnInaccessibleClass() { + PublicInterface object = PublicInterface.getInstance(); + JavaObjectWrapper wrapper = new JavaObjectWrapper(object); + PythonLikeObject wrappedFunction = wrapper.$method$__getattribute__(PythonString.valueOf("interfaceMethod")); + assertThat(wrappedFunction).isInstanceOf(PythonLikeFunction.class); + PythonLikeFunction function = (PythonLikeFunction) wrappedFunction; + assertThat(function.$call(List.of(), Map.of(), null)).isEqualTo(PythonString.valueOf("PrivateObject")); + } + + @Test + void testCallingRecordGetter() { + TestObject object = new TestObject("My name"); + JavaObjectWrapper wrapper = new JavaObjectWrapper(object); + PythonLikeObject result = wrapper.__getAttributeOrNull("name"); + assertThat(result).isEqualTo(PythonString.valueOf("My name")); + } + + @Test + void testComparable() { + JavaObjectWrapper v1 = new JavaObjectWrapper(1); + JavaObjectWrapper v2 = new JavaObjectWrapper(2); + JavaObjectWrapper v3 = new JavaObjectWrapper(3); + assertThat(v1.compareTo(v1)).isZero(); + assertThat(v1.compareTo(v2)).isNegative(); + assertThat(v1.compareTo(v3)).isNegative(); + + assertThat(v2.compareTo(v1)).isPositive(); + assertThat(v2.compareTo(v2)).isZero(); + assertThat(v2.compareTo(v3)).isNegative(); + + assertThat(v3.compareTo(v1)).isPositive(); + assertThat(v3.compareTo(v2)).isPositive(); + assertThat(v3.compareTo(v3)).isZero(); + } + + @Test + void testIterable() { + JavaObjectWrapper iterable = new JavaObjectWrapper(List.of(1, 2, 3)); + assertThat((Iterable) iterable) + .containsExactly( + new JavaObjectWrapper(1), + new JavaObjectWrapper(2), + new JavaObjectWrapper(3)); + } + + @Test + void testLength() { + JavaObjectWrapper iterable = new JavaObjectWrapper(List.of(1, 2, 3)); + PythonLikeObject object = iterable.$method$__getattribute__( + PythonString.valueOf(PythonUnaryOperator.LENGTH.getDunderMethod())); + assertThat(object).isInstanceOf(PythonLikeFunction.class); + PythonLikeFunction function = (PythonLikeFunction) object; + assertThat(function.$call(List.of(), Map.of(), null)).isEqualTo(PythonInteger.valueOf(3)); + } + + @Test + void testGetList() { + JavaObjectWrapper iterable = new JavaObjectWrapper(List.of(1, 2, 3)); + PythonLikeObject object = iterable.$method$__getattribute__( + PythonString.valueOf(PythonBinaryOperator.GET_ITEM.getDunderMethod())); + assertThat(object).isInstanceOf(PythonLikeFunction.class); + PythonLikeFunction function = (PythonLikeFunction) object; + assertThat(function.$call(List.of(PythonInteger.valueOf(0)), Map.of(), null)).isEqualTo(PythonInteger.valueOf(1)); + assertThat(function.$call(List.of(PythonInteger.valueOf(1)), Map.of(), null)).isEqualTo(PythonInteger.valueOf(2)); + assertThat(function.$call(List.of(PythonInteger.valueOf(2)), Map.of(), null)).isEqualTo(PythonInteger.valueOf(3)); + } + + @Test + void testGetMap() { + JavaObjectWrapper iterable = new JavaObjectWrapper(Map.of("a", 1, "b", 2)); + PythonLikeObject object = iterable.$method$__getattribute__( + PythonString.valueOf(PythonBinaryOperator.GET_ITEM.getDunderMethod())); + assertThat(object).isInstanceOf(PythonLikeFunction.class); + PythonLikeFunction function = (PythonLikeFunction) object; + assertThat(function.$call(List.of(PythonString.valueOf("a")), Map.of(), null)).isEqualTo(PythonInteger.valueOf(1)); + assertThat(function.$call(List.of(PythonString.valueOf("b")), Map.of(), null)).isEqualTo(PythonInteger.valueOf(2)); + } + + @Test + void testContains() { + JavaObjectWrapper iterable = new JavaObjectWrapper(List.of(1, 2, 3)); + PythonLikeObject object = iterable.$method$__getattribute__( + PythonString.valueOf(PythonBinaryOperator.CONTAINS.getDunderMethod())); + assertThat(object).isInstanceOf(PythonLikeFunction.class); + PythonLikeFunction function = (PythonLikeFunction) object; + assertThat(function.$call(List.of(PythonInteger.valueOf(0)), Map.of(), null)).isEqualTo(PythonBoolean.FALSE); + assertThat(function.$call(List.of(PythonInteger.valueOf(2)), Map.of(), null)).isEqualTo(PythonBoolean.TRUE); + } +} diff --git a/jpyinterpreter/src/test/java/ai/timefold/jpyinterpreter/types/wrappers/inaccessible/PrivateObject.java b/jpyinterpreter/src/test/java/ai/timefold/jpyinterpreter/types/wrappers/inaccessible/PrivateObject.java new file mode 100644 index 00000000..77942208 --- /dev/null +++ b/jpyinterpreter/src/test/java/ai/timefold/jpyinterpreter/types/wrappers/inaccessible/PrivateObject.java @@ -0,0 +1,7 @@ +package ai.timefold.jpyinterpreter.types.wrappers.inaccessible; + +record PrivateObject() implements PublicInterface { + public String interfaceMethod() { + return "PrivateObject"; + } +} diff --git a/jpyinterpreter/src/test/java/ai/timefold/jpyinterpreter/types/wrappers/inaccessible/PublicInterface.java b/jpyinterpreter/src/test/java/ai/timefold/jpyinterpreter/types/wrappers/inaccessible/PublicInterface.java new file mode 100644 index 00000000..907f8075 --- /dev/null +++ b/jpyinterpreter/src/test/java/ai/timefold/jpyinterpreter/types/wrappers/inaccessible/PublicInterface.java @@ -0,0 +1,9 @@ +package ai.timefold.jpyinterpreter.types.wrappers.inaccessible; + +public interface PublicInterface { + String interfaceMethod(); + + static PublicInterface getInstance() { + return new PrivateObject(); + } +} diff --git a/jpyinterpreter/tox.ini b/jpyinterpreter/tox.ini index 5b355317..41652c67 100644 --- a/jpyinterpreter/tox.ini +++ b/jpyinterpreter/tox.ini @@ -4,9 +4,10 @@ # and then run "tox" from this directory. [tox] -envlist = py39,py310,py311 +env_list = py39,py310,py311 [testenv] +pass_env = * # needed by tox4, to pass JAVA_HOME deps = pytest pytest-cov diff --git a/pom.xml b/pom.xml index 026e75b1..3f6c1435 100644 --- a/pom.xml +++ b/pom.xml @@ -14,7 +14,7 @@ UTF-8 4.4 - + 17 ${project.artifactId} timefold src/main/java,src/main/python diff --git a/tests/test_collectors.py b/tests/test_collectors.py index aba8f51d..9823302a 100644 --- a/tests/test_collectors.py +++ b/tests/test_collectors.py @@ -54,7 +54,7 @@ def set_score(self, score): self.score = score -def create_score_manage(constraint_provider): +def create_score_manager(constraint_provider): return timefold.solver.score_manager_create(timefold.solver.solver_factory_create(timefold.solver.config.solver.SolverConfig() .withSolutionClass(Solution) .withEntityClasses(Entity) @@ -70,7 +70,7 @@ def define_constraints(constraint_factory: timefold.solver.constraint.Constraint .reward('Min value', timefold.solver.score.SimpleScore.ONE, lambda min_value: min_value) ] - score_manager = create_score_manage(define_constraints) + score_manager = create_score_manager(define_constraints) entity_a: Entity = Entity('A') entity_b: Entity = Entity('B') @@ -102,7 +102,7 @@ def define_constraints(constraint_factory: timefold.solver.constraint.Constraint .reward('Max value', timefold.solver.score.SimpleScore.ONE, lambda max_value: max_value) ] - score_manager = create_score_manage(define_constraints) + score_manager = create_score_manager(define_constraints) entity_a: Entity = Entity('A') entity_b: Entity = Entity('B') @@ -134,7 +134,7 @@ def define_constraints(constraint_factory: timefold.solver.constraint.Constraint .reward('Sum value', timefold.solver.score.SimpleScore.ONE, lambda sum_value: sum_value) ] - score_manager = create_score_manage(define_constraints) + score_manager = create_score_manager(define_constraints) entity_a: Entity = Entity('A') entity_b: Entity = Entity('B') @@ -166,7 +166,7 @@ def define_constraints(constraint_factory: timefold.solver.constraint.Constraint .reward('Average value', timefold.solver.score.SimpleScore.ONE, lambda average_value: int(10 * average_value)) ] - score_manager = create_score_manage(define_constraints) + score_manager = create_score_manager(define_constraints) entity_a: Entity = Entity('A') entity_b: Entity = Entity('B') @@ -199,7 +199,7 @@ def define_constraints(constraint_factory: timefold.solver.constraint.Constraint .reward('Count value', timefold.solver.score.SimpleScore.ONE, lambda count: count) ] - score_manager = create_score_manage(define_constraints) + score_manager = create_score_manager(define_constraints) entity_a1: Entity = Entity('A1') entity_a2: Entity = Entity('A2') @@ -225,7 +225,7 @@ def define_constraints(constraint_factory: timefold.solver.constraint.Constraint .reward('Count distinct value', timefold.solver.score.SimpleScore.ONE, lambda count: count) ] - score_manager = create_score_manage(define_constraints) + score_manager = create_score_manager(define_constraints) entity_a: Entity = Entity('A') entity_b: Entity = Entity('B') @@ -248,6 +248,65 @@ def define_constraints(constraint_factory: timefold.solver.constraint.Constraint assert score_manager.explainScore(problem).getScore() == timefold.solver.score.SimpleScore.of(1) +def test_to_consecutive_sequences(): + @timefold.solver.constraint_provider + def define_constraints(constraint_factory: timefold.solver.constraint.ConstraintFactory): + return [ + constraint_factory.for_each(Entity) + .group_by(timefold.solver.constraint.ConstraintCollectors.to_consecutive_sequences( + lambda entity: entity.value.number)) + .flatten_last(lambda sequences: sequences.getConsecutiveSequences()) + .reward('squared sequence length', timefold.solver.score.SimpleScore.ONE, + lambda sequence: sequence.getCount() ** 2) + ] + + score_manager = create_score_manager(define_constraints) + + entity_a: Entity = Entity('A') + entity_b: Entity = Entity('B') + entity_c: Entity = Entity('C') + entity_d: Entity = Entity('D') + entity_e: Entity = Entity('E') + + value_1 = Value(1) + value_2 = Value(2) + value_3 = Value(3) + value_4 = Value(4) + value_5 = Value(5) + value_6 = Value(6) + value_7 = Value(7) + value_8 = Value(8) + value_9 = Value(9) + + problem = Solution([entity_a, entity_b, entity_c, entity_d, entity_e], + [value_1, value_2, value_3, value_4, value_5, + value_6, value_7, value_8, value_9]) + + entity_a.set_value(value_1) + entity_b.set_value(value_3) + entity_c.set_value(value_5) + entity_d.set_value(value_7) + entity_e.set_value(value_9) + + assert score_manager.explainScore(problem).getScore().score() == 5 + + entity_a.set_value(value_1) + entity_b.set_value(value_2) + entity_c.set_value(value_3) + entity_d.set_value(value_4) + entity_e.set_value(value_5) + + assert score_manager.explainScore(problem).getScore().score() == 25 + + entity_a.set_value(value_1) + entity_b.set_value(value_2) + entity_c.set_value(value_3) + entity_d.set_value(value_5) + entity_e.set_value(value_6) + + assert score_manager.explainScore(problem).getScore().score() == 13 + + def test_to_list(): @timefold.solver.constraint_provider def define_constraints(constraint_factory: timefold.solver.constraint.ConstraintFactory): @@ -257,7 +316,7 @@ def define_constraints(constraint_factory: timefold.solver.constraint.Constraint .reward('list size', timefold.solver.score.SimpleScore.ONE, lambda values: len(values)) ] - score_manager = create_score_manage(define_constraints) + score_manager = create_score_manager(define_constraints) entity_a: Entity = Entity('A') entity_b: Entity = Entity('B') @@ -289,7 +348,7 @@ def define_constraints(constraint_factory: timefold.solver.constraint.Constraint .reward('set size', timefold.solver.score.SimpleScore.ONE, lambda values: len(values)) ] - score_manager = create_score_manage(define_constraints) + score_manager = create_score_manager(define_constraints) entity_a: Entity = Entity('A') entity_b: Entity = Entity('B') @@ -322,7 +381,7 @@ def define_constraints(constraint_factory: timefold.solver.constraint.Constraint .reward('map at B', timefold.solver.score.SimpleScore.ONE, lambda entity_map: next(iter(entity_map['B']))) ] - score_manager = create_score_manage(define_constraints) + score_manager = create_score_manager(define_constraints) entity_a: Entity = Entity('A') entity_b: Entity = Entity('B') @@ -354,7 +413,7 @@ def define_constraints(constraint_factory: timefold.solver.constraint.Constraint .reward('min', timefold.solver.score.SimpleScore.ONE, lambda values: next(iter(values))) ] - score_manager = create_score_manage(define_constraints) + score_manager = create_score_manager(define_constraints) entity_a: Entity = Entity('A') entity_b: Entity = Entity('B') @@ -387,7 +446,7 @@ def define_constraints(constraint_factory: timefold.solver.constraint.Constraint .reward('map at A', timefold.solver.score.SimpleScore.ONE, lambda entity_map: next(iter(entity_map['A']))) ] - score_manager = create_score_manage(define_constraints) + score_manager = create_score_manager(define_constraints) entity_a: Entity = Entity('A') entity_b: Entity = Entity('B') @@ -424,7 +483,7 @@ def define_constraints(constraint_factory: timefold.solver.constraint.Constraint .reward('Conditionally count value', timefold.solver.score.SimpleScore.ONE, lambda count: count) ] - score_manager = create_score_manage(define_constraints) + score_manager = create_score_manager(define_constraints) entity_a1: Entity = Entity('A1') entity_a2: Entity = Entity('A2') @@ -455,7 +514,7 @@ def define_constraints(constraint_factory: timefold.solver.constraint.Constraint # min is in lower digit; max in upper digit ] - score_manager = create_score_manage(define_constraints) + score_manager = create_score_manager(define_constraints) entity_a: Entity = Entity('A') entity_b: Entity = Entity('B') @@ -477,6 +536,40 @@ def define_constraints(constraint_factory: timefold.solver.constraint.Constraint assert score_manager.explainScore(problem).getScore() == timefold.solver.score.SimpleScore.of(22) +def test_collect_and_then(): + @timefold.solver.constraint_provider + def define_constraints(constraint_factory: timefold.solver.constraint.ConstraintFactory): + return [ + constraint_factory.for_each(Entity) + .group_by(timefold.solver.constraint.ConstraintCollectors.collect_and_then( + timefold.solver.constraint.ConstraintCollectors.min(lambda entity: entity.value.number), + lambda a: 2 * a + )) + .reward('Double min value', timefold.solver.score.SimpleScore.ONE, lambda twice_min: twice_min) + ] + + score_manager = create_score_manager(define_constraints) + + entity_a: Entity = Entity('A') + entity_b: Entity = Entity('B') + + value_1 = Value(1) + value_2 = Value(2) + + problem = Solution([entity_a, entity_b], [value_1, value_2]) + entity_a.set_value(value_1) + entity_b.set_value(value_1) + + assert score_manager.explainScore(problem).getScore() == timefold.solver.score.SimpleScore.of(2) + + entity_a.set_value(value_2) + + assert score_manager.explainScore(problem).getScore() == timefold.solver.score.SimpleScore.of(2) + + entity_b.set_value(value_2) + + assert score_manager.explainScore(problem).getScore() == timefold.solver.score.SimpleScore.of(4) + def test_flatten_last(): @timefold.solver.constraint_provider @@ -488,7 +581,7 @@ def define_constraints(constraint_factory: timefold.solver.constraint.Constraint .reward('Count', timefold.solver.score.SimpleScore.ONE) ] - score_manager = create_score_manage(define_constraints) + score_manager = create_score_manager(define_constraints) entity_a: Entity = Entity('A') diff --git a/tests/test_constraint_streams.py b/tests/test_constraint_streams.py new file mode 100644 index 00000000..2183b591 --- /dev/null +++ b/tests/test_constraint_streams.py @@ -0,0 +1,534 @@ +import inspect +import re +import timefold.solver +import timefold.solver.score +import timefold.solver.config +import timefold.solver.constraint +from timefold.solver.constraint import UniConstraintStream, BiConstraintStream, TriConstraintStream, QuadConstraintStream, \ + Joiners, ConstraintCollectors, ConstraintFactory +from ai.timefold.solver.core.api.score.stream import Joiners as JavaJoiners,\ + ConstraintCollectors as JavaConstraintCollectors, ConstraintFactory as JavaConstraintFactory +from ai.timefold.solver.core.api.score.stream.uni import UniConstraintStream as JavaUniConstraintStream +from ai.timefold.solver.core.api.score.stream.bi import BiConstraintStream as JavaBiConstraintStream +from ai.timefold.solver.core.api.score.stream.tri import TriConstraintStream as JavaTriConstraintStream +from ai.timefold.solver.core.api.score.stream.quad import QuadConstraintStream as JavaQuadConstraintStream + +@timefold.solver.problem_fact +class Value: + def __init__(self, number): + self.number = number + + +@timefold.solver.planning_entity +class Entity: + def __init__(self, code, value=None): + self.code = code + self.value = value + + @timefold.solver.planning_variable(Value, ['value_range']) + def get_value(self): + return self.value + + def set_value(self, value): + self.value = value + + +@timefold.solver.planning_solution +class Solution: + def __init__(self, entity_list, value_list, score=None): + self.entity_list = entity_list + self.value_list = value_list + self.score = score + + @timefold.solver.planning_entity_collection_property(Entity) + def get_entity_list(self): + return self.entity_list + + def set_entity_list(self, entity_list): + self.entity_list = entity_list + + @timefold.solver.problem_fact_collection_property(Value) + @timefold.solver.value_range_provider('value_range') + def get_value_list(self): + return self.value_list + + def set_value_list(self, value_list): + self.value_list = value_list + + @timefold.solver.planning_score(timefold.solver.score.SimpleScore) + def get_score(self): + return self.score + + def set_score(self, score): + self.score = score + + +def create_score_manager(constraint_provider): + return timefold.solver.score_manager_create(timefold.solver.solver_factory_create(timefold.solver.config.solver.SolverConfig() + .withSolutionClass(Solution) + .withEntityClasses(Entity) + .withConstraintProviderClass(constraint_provider))) + + +def test_for_each(): + @timefold.solver.constraint_provider + def define_constraints(constraint_factory: timefold.solver.constraint.ConstraintFactory): + return [ + constraint_factory.for_each(Entity) + .reward('Count', timefold.solver.score.SimpleScore.ONE) + ] + score_manager = create_score_manager(define_constraints) + entity_a: Entity = Entity('A') + entity_b: Entity = Entity('B') + + value_1 = Value(1) + + problem = Solution([entity_a, entity_b], [value_1]) + + assert score_manager.explainScore(problem).getScore().score() == 0 + + entity_a.set_value(value_1) + + assert score_manager.explainScore(problem).getScore().score() == 1 + + entity_b.set_value(value_1) + + assert score_manager.explainScore(problem).getScore().score() == 2 + + +def test_filter_uni(): + @timefold.solver.constraint_provider + def define_constraints(constraint_factory: timefold.solver.constraint.ConstraintFactory): + return [ + constraint_factory.for_each(Entity) + .filter(lambda e: e.value.number == 1) + .reward('Count', timefold.solver.score.SimpleScore.ONE) + ] + + score_manager = create_score_manager(define_constraints) + entity_a: Entity = Entity('A') + entity_b: Entity = Entity('B') + + value_1 = Value(1) + value_2 = Value(2) + + problem = Solution([entity_a, entity_b], [value_1, value_2]) + + assert score_manager.explainScore(problem).getScore().score() == 0 + entity_a.set_value(value_1) + + assert score_manager.explainScore(problem).getScore().score() == 1 + + entity_b.set_value(value_2) + assert score_manager.explainScore(problem).getScore().score() == 1 + + entity_b.set_value(value_1) + assert score_manager.explainScore(problem).getScore().score() == 2 + + +def test_filter_bi(): + @timefold.solver.constraint_provider + def define_constraints(constraint_factory: timefold.solver.constraint.ConstraintFactory): + return [ + constraint_factory.for_each(Entity) + .join(Entity) + .filter(lambda e1, e2: e1.value.number == 1 and e2.value.number == 2) + .reward('Count', timefold.solver.score.SimpleScore.ONE) + ] + + score_manager = create_score_manager(define_constraints) + entity_a: Entity = Entity('A') + entity_b: Entity = Entity('B') + + value_1 = Value(1) + value_2 = Value(2) + + problem = Solution([entity_a, entity_b], [value_1, value_2]) + + assert score_manager.explainScore(problem).getScore().score() == 0 + entity_a.set_value(value_1) + + assert score_manager.explainScore(problem).getScore().score() == 0 + + entity_b.set_value(value_1) + assert score_manager.explainScore(problem).getScore().score() == 0 + + entity_b.set_value(value_2) + assert score_manager.explainScore(problem).getScore().score() == 1 + + +def test_filter_tri(): + @timefold.solver.constraint_provider + def define_constraints(constraint_factory: timefold.solver.constraint.ConstraintFactory): + return [ + constraint_factory.for_each(Entity) + .join(Entity) + .join(Entity) + .filter(lambda e1, e2, e3: e1.value.number == 1 and e2.value.number == 2 and e3.value.number == 3) + .reward('Count', timefold.solver.score.SimpleScore.ONE) + ] + + score_manager = create_score_manager(define_constraints) + entity_a: Entity = Entity('A') + entity_b: Entity = Entity('B') + entity_c: Entity = Entity('C') + + value_1 = Value(1) + value_2 = Value(2) + value_3 = Value(3) + + problem = Solution([entity_a, entity_b, entity_c], [value_1, value_2, value_3]) + + assert score_manager.explainScore(problem).getScore().score() == 0 + entity_a.set_value(value_1) + + assert score_manager.explainScore(problem).getScore().score() == 0 + + entity_b.set_value(value_2) + assert score_manager.explainScore(problem).getScore().score() == 0 + + entity_c.set_value(value_1) + assert score_manager.explainScore(problem).getScore().score() == 0 + + entity_c.set_value(value_3) + assert score_manager.explainScore(problem).getScore().score() == 1 + + +def test_filter_quad(): + @timefold.solver.constraint_provider + def define_constraints(constraint_factory: timefold.solver.constraint.ConstraintFactory): + return [ + constraint_factory.for_each(Entity) + .join(Entity) + .join(Entity) + .join(Entity) + .filter(lambda e1, e2, e3, e4: e1.value.number == 1 and e2.value.number == 2 and e3.value.number == 3 + and e4.value.number == 4) + .reward('Count', timefold.solver.score.SimpleScore.ONE) + ] + + score_manager = create_score_manager(define_constraints) + entity_a: Entity = Entity('A') + entity_b: Entity = Entity('B') + entity_c: Entity = Entity('C') + entity_d: Entity = Entity('D') + + value_1 = Value(1) + value_2 = Value(2) + value_3 = Value(3) + value_4 = Value(4) + + problem = Solution([entity_a, entity_b, entity_c, entity_d], [value_1, value_2, value_3, value_4]) + + assert score_manager.explainScore(problem).getScore().score() == 0 + entity_a.set_value(value_1) + + assert score_manager.explainScore(problem).getScore().score() == 0 + + entity_b.set_value(value_2) + assert score_manager.explainScore(problem).getScore().score() == 0 + + entity_c.set_value(value_3) + assert score_manager.explainScore(problem).getScore().score() == 0 + + entity_d.set_value(value_1) + assert score_manager.explainScore(problem).getScore().score() == 0 + + entity_d.set_value(value_4) + assert score_manager.explainScore(problem).getScore().score() == 1 + + +def test_join_uni(): + @timefold.solver.constraint_provider + def define_constraints(constraint_factory: timefold.solver.constraint.ConstraintFactory): + return [ + constraint_factory.for_each(Entity) + .join(Entity, timefold.solver.constraint.Joiners.equal(lambda entity: entity.code)) + .filter(lambda e1, e2: e1 != e2) + .reward('Count', timefold.solver.score.SimpleScore.ONE, lambda e1, e2: e1.value.number * e2.value.number) + ] + score_manager = create_score_manager(define_constraints) + entity_a1: Entity = Entity('A') + entity_a2: Entity = Entity('A') + entity_b1: Entity = Entity('B') + entity_b2: Entity = Entity('B') + + value_1 = Value(1) + value_2 = Value(2) + + problem = Solution([entity_a1, entity_a2, entity_b1, entity_b2], [value_1, value_2]) + + entity_a1.set_value(value_1) + + assert score_manager.explainScore(problem).getScore().score() == 0 + + entity_a1.set_value(value_1) + entity_a2.set_value(value_1) + + entity_b1.set_value(value_2) + entity_b2.set_value(value_2) + + # 1 * 1 + 1 * 1 + 2 * 2 + 2 * 2 + assert score_manager.explainScore(problem).getScore().score() == 10 + + entity_a1.set_value(value_2) + entity_b1.set_value(value_1) + + # 1 * 2 + 1 * 2 + 1 * 2 + 1 * 2 + assert score_manager.explainScore(problem).getScore().score() == 8 + + +def test_map(): + @timefold.solver.constraint_provider + def define_constraints(constraint_factory: timefold.solver.constraint.ConstraintFactory): + return [ + constraint_factory.for_each(Entity) + .map(lambda e: e.value.number) + .reward('Count', timefold.solver.score.SimpleScore.ONE, lambda v: v) + ] + + score_manager = create_score_manager(define_constraints) + entity_a: Entity = Entity('A') + entity_b: Entity = Entity('B') + + value_1 = Value(1) + value_2 = Value(2) + + problem = Solution([entity_a, entity_b], [value_1, value_2]) + + assert score_manager.explainScore(problem).getScore().score() == 0 + + entity_a.set_value(value_1) + + assert score_manager.explainScore(problem).getScore().score() == 1 + + entity_b.set_value(value_1) + + assert score_manager.explainScore(problem).getScore().score() == 2 + + entity_b.set_value(value_2) + + assert score_manager.explainScore(problem).getScore().score() == 3 + + +def test_multi_map(): + @timefold.solver.constraint_provider + def define_constraints(constraint_factory: timefold.solver.constraint.ConstraintFactory): + return [ + constraint_factory.for_each(Entity) + .map(lambda e: e.code, lambda e: e.value.number) + .reward('Count', timefold.solver.score.SimpleScore.ONE, lambda c, v: len(c) + v) + ] + + score_manager = create_score_manager(define_constraints) + entity_a: Entity = Entity('A') + entity_b: Entity = Entity('BB') + + value_1 = Value(10) + value_2 = Value(20) + + problem = Solution([entity_a, entity_b], [value_1, value_2]) + + assert score_manager.explainScore(problem).getScore().score() == 0 + + entity_a.set_value(value_1) + + assert score_manager.explainScore(problem).getScore().score() == 11 + + entity_b.set_value(value_1) + + assert score_manager.explainScore(problem).getScore().score() == 23 + + entity_b.set_value(value_2) + + assert score_manager.explainScore(problem).getScore().score() == 33 + + +def test_expand(): + @timefold.solver.constraint_provider + def define_constraints(constraint_factory: timefold.solver.constraint.ConstraintFactory): + return [ + constraint_factory.for_each(Entity) + .expand(lambda e: e.value.number) + .reward('Count', timefold.solver.score.SimpleScore.ONE, lambda e, v: v) + ] + + score_manager = create_score_manager(define_constraints) + entity_a: Entity = Entity('A') + entity_b: Entity = Entity('B') + + value_1 = Value(1) + value_2 = Value(2) + + problem = Solution([entity_a, entity_b], [value_1, value_2]) + + assert score_manager.explainScore(problem).getScore().score() == 0 + + entity_a.set_value(value_1) + + assert score_manager.explainScore(problem).getScore().score() == 1 + + entity_b.set_value(value_1) + + assert score_manager.explainScore(problem).getScore().score() == 2 + + entity_b.set_value(value_2) + + assert score_manager.explainScore(problem).getScore().score() == 3 + + +def test_multi_expand(): + @timefold.solver.constraint_provider + def define_constraints(constraint_factory: timefold.solver.constraint.ConstraintFactory): + return [ + constraint_factory.for_each(Entity) + .expand(lambda e: e.code, lambda e: e.value.number) + .reward('Count', timefold.solver.score.SimpleScore.ONE, lambda e, c, v: len(c) + v) + ] + + score_manager = create_score_manager(define_constraints) + entity_a: Entity = Entity('A') + entity_b: Entity = Entity('BB') + + value_1 = Value(10) + value_2 = Value(20) + + problem = Solution([entity_a, entity_b], [value_1, value_2]) + + assert score_manager.explainScore(problem).getScore().score() == 0 + + entity_a.set_value(value_1) + + assert score_manager.explainScore(problem).getScore().score() == 11 + + entity_b.set_value(value_1) + + assert score_manager.explainScore(problem).getScore().score() == 23 + + entity_b.set_value(value_2) + + assert score_manager.explainScore(problem).getScore().score() == 33 + + +def test_concat(): + @timefold.solver.constraint_provider + def define_constraints(constraint_factory: timefold.solver.constraint.ConstraintFactory): + return [ + constraint_factory.for_each(Entity) + .filter(lambda e: e.value.number == 1) + .concat(constraint_factory.for_each(Entity).filter(lambda e: e.value.number == 2)) + .reward('Count', timefold.solver.score.SimpleScore.ONE) + ] + + score_manager = create_score_manager(define_constraints) + entity_a: Entity = Entity('A') + entity_b: Entity = Entity('B') + + value_1 = Value(1) + value_2 = Value(2) + value_3 = Value(3) + + problem = Solution([entity_a, entity_b], [value_1, value_2, value_3]) + + assert score_manager.explainScore(problem).getScore().score() == 0 + + entity_a.set_value(value_1) + + assert score_manager.explainScore(problem).getScore().score() == 1 + + entity_b.set_value(value_2) + + assert score_manager.explainScore(problem).getScore().score() == 2 + + entity_b.set_value(value_3) + + assert score_manager.explainScore(problem).getScore().score() == 1 + + +ignored_python_functions = { + '_call_comparison_java_joiner', + '__init__', + 'from_', # ignored since the camelcase version is from, which is a keyword in Python +} + +ignored_java_functions = { + 'equals', + 'getClass', + 'hashCode', + 'notify', + 'notifyAll', + 'toString', + 'wait', + 'countLongBi', # Python has no concept of Long (everything a BigInteger) + 'countLongQuad', + 'countLongTri', + '_handler', # JPype handler field should be ignored +} + +def test_camel_case_for_all_snake_case_methods(): + for class_type in (UniConstraintStream, BiConstraintStream, TriConstraintStream, QuadConstraintStream, Joiners, + ConstraintCollectors, ConstraintFactory): + missing = [] + incorrect = [] + for function in inspect.getmembers(class_type, inspect.isfunction): + # split underscore using split + function_name = function[0] + if function_name in ignored_python_functions: + continue + function_name_parts = function_name.split('_') + + # joining result + camel_case_name = function_name_parts[0] + ''.join(ele.title() for ele in function_name_parts[1:]) + if not hasattr(class_type, camel_case_name): + missing.append(camel_case_name) + if getattr(class_type, camel_case_name) is not function[1]: + incorrect.append(function) + + assert len(missing) == 0 + assert len(incorrect) == 0 + + +def test_snake_case_for_all_camel_case_methods(): + for class_type in (UniConstraintStream, BiConstraintStream, TriConstraintStream, QuadConstraintStream, Joiners, + ConstraintCollectors, ConstraintFactory): + missing = [] + incorrect = [] + for function in inspect.getmembers(class_type, inspect.isfunction): + # split underscore using split + function_name = function[0] + if function_name in ignored_python_functions: + continue + snake_case_name = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', function_name) + # change h_t_t_p -> http + snake_case_name = re.sub('([a-z0-9])([A-Z])', r'\1_\2', snake_case_name).lower() + + if not hasattr(class_type, snake_case_name): + missing.append(snake_case_name) + if getattr(class_type, snake_case_name) is not function[1]: + incorrect.append(function) + + assert len(missing) == 0 + assert len(incorrect) == 0 + + +def test_has_all_methods(): + + for python_type, java_type in ((UniConstraintStream, JavaUniConstraintStream), + (BiConstraintStream, JavaBiConstraintStream), + (TriConstraintStream, JavaTriConstraintStream), + (QuadConstraintStream, JavaQuadConstraintStream), + (Joiners, JavaJoiners), + (ConstraintCollectors, JavaConstraintCollectors), + (ConstraintFactory, JavaConstraintFactory)): + missing = [] + for function_name, function_impl in inspect.getmembers(java_type, inspect.isfunction): + if function_name in ignored_java_functions: + continue + if python_type is ConstraintCollectors and function_name.endswith(('Long', 'BigInteger', 'Duration', + 'BigDecimal', 'Period')): + continue # Python only has a single integer type (= BigInteger) and does not support Java Durations + # or Period + if not hasattr(python_type, function_name): + missing.append(function_name) + + assert len(missing) == 0 diff --git a/tests/test_java_cs_methods.py b/tests/test_java_cs_methods.py deleted file mode 100644 index 0622f7bb..00000000 --- a/tests/test_java_cs_methods.py +++ /dev/null @@ -1,99 +0,0 @@ -import inspect -import re -from timefold.solver.constraint import UniConstraintStream, BiConstraintStream, TriConstraintStream, QuadConstraintStream, \ - Joiners, ConstraintCollectors, ConstraintFactory -from ai.timefold.solver.core.api.score.stream import Joiners as JavaJoiners,\ - ConstraintCollectors as JavaConstraintCollectors, ConstraintFactory as JavaConstraintFactory -from ai.timefold.solver.core.api.score.stream.uni import UniConstraintStream as JavaUniConstraintStream -from ai.timefold.solver.core.api.score.stream.bi import BiConstraintStream as JavaBiConstraintStream -from ai.timefold.solver.core.api.score.stream.tri import TriConstraintStream as JavaTriConstraintStream -from ai.timefold.solver.core.api.score.stream.quad import QuadConstraintStream as JavaQuadConstraintStream - -ignored_python_functions = { - '_call_comparison_java_joiner', - '__init__', - 'from_', # ignored since the camelcase version is from, which is a keyword in Python -} - -ignored_java_functions = { - 'equals', - 'getClass', - 'hashCode', - 'notify', - 'notifyAll', - 'toString', - 'wait', - 'countLongBi', # Python has no concept of Long (everything a BigInteger) - 'countLongQuad', - 'countLongTri', - '_handler', # JPype handler field should be ignored -} - -def test_camel_case_for_all_snake_case_methods(): - for class_type in (UniConstraintStream, BiConstraintStream, TriConstraintStream, QuadConstraintStream, Joiners, - ConstraintCollectors, ConstraintFactory): - missing = [] - incorrect = [] - for function in inspect.getmembers(class_type, inspect.isfunction): - # split underscore using split - function_name = function[0] - if function_name in ignored_python_functions: - continue - function_name_parts = function_name.split('_') - - # joining result - camel_case_name = function_name_parts[0] + ''.join(ele.title() for ele in function_name_parts[1:]) - if not hasattr(class_type, camel_case_name): - missing.append(camel_case_name) - if getattr(class_type, camel_case_name) is not function[1]: - incorrect.append(function) - - assert len(missing) == 0 - assert len(incorrect) == 0 - - -def test_snake_case_for_all_camel_case_methods(): - for class_type in (UniConstraintStream, BiConstraintStream, TriConstraintStream, QuadConstraintStream, Joiners, - ConstraintCollectors, ConstraintFactory): - missing = [] - incorrect = [] - for function in inspect.getmembers(class_type, inspect.isfunction): - # split underscore using split - function_name = function[0] - if function_name in ignored_python_functions: - continue - snake_case_name = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', function_name) - # change h_t_t_p -> http - snake_case_name = re.sub('([a-z0-9])([A-Z])', r'\1_\2', snake_case_name).lower() - - if not hasattr(class_type, snake_case_name): - missing.append(snake_case_name) - if getattr(class_type, snake_case_name) is not function[1]: - incorrect.append(function) - - assert len(missing) == 0 - assert len(incorrect) == 0 - - -def test_has_all_methods(): - - for python_type, java_type in ((UniConstraintStream, JavaUniConstraintStream), - (BiConstraintStream, JavaBiConstraintStream), - (TriConstraintStream, JavaTriConstraintStream), - (QuadConstraintStream, JavaQuadConstraintStream), - (Joiners, JavaJoiners), - (ConstraintCollectors, JavaConstraintCollectors), - (ConstraintFactory, JavaConstraintFactory)): - missing = [] - for function_name, function_impl in inspect.getmembers(java_type, inspect.isfunction): - if function_name in ignored_java_functions: - continue - if python_type is ConstraintCollectors and function_name.endswith(('Long', 'BigInteger', 'Duration', - 'BigDecimal', 'Period')): - continue # Python only has a single integer type (= BigInteger) and does not support Java Durations - # or Period - if not hasattr(python_type, function_name): - missing.append(function_name) - - # TODO: Implement expand and concat - assert len(missing) <= 2 diff --git a/timefold-solver-python-core/src/main/python/constraint/__init__.py b/timefold-solver-python-core/src/main/python/constraint/__init__.py index 475541be..7613351e 100644 --- a/timefold-solver-python-core/src/main/python/constraint/__init__.py +++ b/timefold-solver-python-core/src/main/python/constraint/__init__.py @@ -12,6 +12,7 @@ from ..constraint_stream import PythonConstraintFactory as ConstraintFactory, \ PythonUniConstraintStream as UniConstraintStream, PythonBiConstraintStream as BiConstraintStream, \ PythonTriConstraintStream as TriConstraintStream, PythonQuadConstraintStream as QuadConstraintStream +from ai.timefold.solver.core.api.score.stream.common import SequenceChain from ai.timefold.solver.core.api.score.stream.uni import UniConstraintCollector from ai.timefold.solver.core.api.score.stream.bi import BiJoiner, BiConstraintCollector from ai.timefold.solver.core.api.score.stream.tri import TriJoiner, TriConstraintCollector @@ -659,6 +660,49 @@ def conditionally(predicate, delegate): predicate, delegate) + @overload # noqa + @staticmethod + def collect_and_then(delegate: 'UniConstraintCollector[A, Any, A_]', + mapping_function: Callable[[A_], B_]) -> \ + 'UniConstraintCollector[A, Any, B_]': + ... + + @overload # noqa + @staticmethod + def collect_and_then(delegate: 'BiConstraintCollector[A, B, Any, A_]', + mapping_function: Callable[[A_], B_]) -> 'BiConstraintCollector[A, B, Any, B_]': + ... + + @overload # noqa + @staticmethod + def collect_and_then(delegate: 'TriConstraintCollector[A, B, C, Any, A_]', + mapping_function: Callable[[A_], B_]) -> \ + 'TriConstraintCollector[A, B, C, Any, B_]': + ... + + @overload # noqa + @staticmethod + def collect_and_then(delegate: 'QuadConstraintCollector[A, B, C, D, Any, A_]', + mapping_function: Callable[[A_], B_]) -> \ + 'QuadConstraintCollector[A, B, C, D, Any, B_]': + ... + + @staticmethod + def collect_and_then(delegate, mapping_function): + """Returns a collector that delegates to the underlying collector and maps its result to another value. + + :param delegate: + :param mapping_function: + + :return: + """ + from ..constraint_stream import CollectAndThenCollector + return CollectAndThenCollector(JavaConstraintCollectors.collectAndThen, + delegate, + mapping_function) + + collectAndThen = collect_and_then + @staticmethod def count() -> 'UniConstraintCollector[A, Any, int]': """Returns a collector that counts the number of elements that are being grouped. @@ -944,6 +988,42 @@ def sum(function, zero=None, adder=None, subtractor=None): else: raise ValueError + @overload # noqa + @staticmethod + def to_consecutive_sequences(index_map: Callable[[A], int]) -> 'UniConstraintCollector[A, Any, SequenceChain[A, int]]': + ... + + @overload # noqa + @staticmethod + def to_consecutive_sequences(result_map: Callable[[A, B], A_], index_map: Callable[[A_], int]) -> \ + 'BiConstraintCollector[A, B, Any, SequenceChain[A_, int]]': + ... + + @overload # noqa + @staticmethod + def to_consecutive_sequences(result_map: Callable[[A, B, C], A_], index_map: Callable[[A_], int]) -> \ + 'TriConstraintCollector[A, B, C, Any, SequenceChain[A_, int]]': + ... + + @overload # noqa + @staticmethod + def to_consecutive_sequences(result_map: Callable[[A, B, C, D], A_], index_map: Callable[[A_], int]) -> \ + 'QuadConstraintCollector[A, B, C, D, Any, SequenceChain[A_, int]]': + ... + + @staticmethod + def to_consecutive_sequences(result_or_index_map, index_map=None): + from ..constraint_stream import (GroupIntMappingSingleArgConstraintCollector, + GroupMappingIntMappingTwoArgConstraintCollector) + if index_map is None: + return GroupIntMappingSingleArgConstraintCollector(JavaConstraintCollectors.toConsecutiveSequences, + result_or_index_map) + else: + return GroupMappingIntMappingTwoArgConstraintCollector(JavaConstraintCollectors.toConsecutiveSequences, + result_or_index_map, index_map) + + toConsecutiveSequences = to_consecutive_sequences + @overload # noqa @staticmethod def to_collection(collection_creator: Callable[[int], B_]) -> 'UniConstraintCollector[A, Any, B_]': diff --git a/timefold-solver-python-core/src/main/python/constraint_stream.py b/timefold-solver-python-core/src/main/python/constraint_stream.py index c5526836..7263e632 100644 --- a/timefold-solver-python-core/src/main/python/constraint_stream.py +++ b/timefold-solver-python-core/src/main/python/constraint_stream.py @@ -433,6 +433,11 @@ class GroupIntMappingSingleArgConstraintCollector: collector_creator: Callable group_mapping: Callable +@dataclasses.dataclass +class GroupMappingIntMappingTwoArgConstraintCollector: + collector_creator: Callable + group_mapping: Callable + index_mapping: Callable @dataclasses.dataclass class ComposeConstraintCollector: @@ -447,6 +452,12 @@ class ConditionalConstraintCollector: predicate: Callable delegate: Any +@dataclasses.dataclass +class CollectAndThenCollector: + collector_creator: Callable + delegate_collector: Any + mapping_function: Callable + def extract_collector(collector_info, *type_arguments): if isinstance(collector_info, NoArgsConstraintCollector): @@ -458,6 +469,9 @@ def extract_collector(collector_info, *type_arguments): function_cast(collector_info.value_mapping, *type_arguments)) elif isinstance(collector_info, GroupIntMappingSingleArgConstraintCollector): return collector_info.collector_creator(to_int_function_cast(collector_info.group_mapping, *type_arguments)) + elif isinstance(collector_info, GroupMappingIntMappingTwoArgConstraintCollector): + return collector_info.collector_creator(function_cast(collector_info.group_mapping, *type_arguments), + to_int_function_cast(collector_info.index_mapping, JClass('java.lang.Object'))) elif isinstance(collector_info, ComposeConstraintCollector): subcollectors = tuple(map(lambda subcollector_info: extract_collector(subcollector_info, *type_arguments), collector_info.subcollectors)) @@ -468,6 +482,10 @@ def extract_collector(collector_info, *type_arguments): delegate_collector = extract_collector(collector_info.delegate, *type_arguments) predicate = predicate_cast(collector_info.predicate, *type_arguments) return collector_info.collector_creator(predicate, delegate_collector) + elif isinstance(collector_info, CollectAndThenCollector): + delegate_collector = extract_collector(collector_info.delegate_collector, *type_arguments) + mapping_function = function_cast(collector_info.mapping_function, JClass('java.lang.Object')) + return collector_info.collector_creator(delegate_collector, mapping_function) else: raise ValueError(f'Invalid Collector: {collector_info}. ' f'Create Collectors via timefold.solver.constraint.ConstraintCollectors.') @@ -934,17 +952,89 @@ def group_by(self, *args): groupBy = group_by - + @overload def map(self, mapping_function: Callable[[A], A_]) -> 'PythonUniConstraintStream[A_]': + ... + + @overload + def map(self, mapping_function: Callable[[A], A_], mapping_function2: Callable[[A], B_]) -> 'PythonBiConstraintStream[A_, B_]': + ... + + @overload + def map(self, mapping_function: Callable[[A], A_], mapping_function2: Callable[[A], B_], + mapping_function3: Callable[[A], C_]) -> 'PythonTriConstraintStream[A_, B_, C_]': + ... + + @overload + def map(self, mapping_function: Callable[[A], A_], mapping_function2: Callable[[A], B_], + mapping_function3: Callable[[A], C_], mapping_function4: Callable[[A], D_]) -> 'PythonQuadConstraintStream[A_, B_, C_, D_]': + ... + + def map(self, *mapping_functions): """Transforms the stream in such a way that tuples are remapped using the given function. :param mapping_function: :return: """ - translated_function = function_cast(mapping_function, self.a_type) - return PythonUniConstraintStream(self.delegate.map(translated_function), self.package, - JClass('java.lang.Object')) + if len(mapping_functions) == 0: + raise ValueError(f'At least one mapping function is required for map.') + if len(mapping_functions) > 4: + raise ValueError(f'At most four mapping functions can be passed to map (got {len(mapping_functions)}).') + translated_functions = tuple(map(lambda mapping_function: function_cast(mapping_function, self.a_type), + mapping_functions)) + if len(mapping_functions) == 1: + return PythonUniConstraintStream(self.delegate.map(*translated_functions), self.package, + JClass('java.lang.Object')) + if len(mapping_functions) == 2: + return PythonBiConstraintStream(self.delegate.map(*translated_functions), self.package, + JClass('java.lang.Object'), JClass('java.lang.Object')) + if len(mapping_functions) == 3: + return PythonTriConstraintStream(self.delegate.map(*translated_functions), self.package, + JClass('java.lang.Object'), JClass('java.lang.Object'), JClass('java.lang.Object')) + if len(mapping_functions) == 4: + return PythonQuadConstraintStream(self.delegate.map(*translated_functions), self.package, + JClass('java.lang.Object'), JClass('java.lang.Object'), JClass('java.lang.Object'), JClass('java.lang.Object')) + raise RuntimeError(f'Impossible state: missing case for {len(mapping_functions)}.') + + @overload + def expand(self, mapping_function: Callable[[A], B_]) -> 'PythonBiConstraintStream[A, B_]': + ... + + @overload + def expand(self, mapping_function: Callable[[A], B_], mapping_function2: Callable[[A], C_]) -> 'PythonTriConstraintStream[A, B_, C_]': + ... + + @overload + def expand(self, mapping_function: Callable[[A], B_], mapping_function2: Callable[[A], C_], + mapping_function3: Callable[[A], D_]) -> 'PythonTriConstraintStream[A, B_, C_, D_]': + ... + + def expand(self, *mapping_functions): + """ + Tuple expansion is a special case of tuple mapping + which only increases stream cardinality and can not introduce duplicate tuples. + It enables you to add extra facts to each tuple in a constraint stream by applying a mapping function to it. + This is useful in situations where an expensive computations needs to be cached for use later in the stream. + :param mapping_functions: + :return: + """ + if len(mapping_functions) == 0: + raise ValueError(f'At least one mapping function is required for expand.') + if len(mapping_functions) > 3: + raise ValueError(f'At most three mapping functions can be passed to expand on a UniStream (got {len(mapping_functions)}).') + translated_functions = tuple(map(lambda mapping_function: function_cast(mapping_function, self.a_type), + mapping_functions)) + if len(mapping_functions) == 1: + return PythonBiConstraintStream(self.delegate.expand(*translated_functions), self.package, + self.a_type, JClass('java.lang.Object')) + if len(mapping_functions) == 2: + return PythonTriConstraintStream(self.delegate.expand(*translated_functions), self.package, + self.a_type, JClass('java.lang.Object'), JClass('java.lang.Object')) + if len(mapping_functions) == 3: + return PythonQuadConstraintStream(self.delegate.expand(*translated_functions), self.package, + self.a_type, JClass('java.lang.Object'), JClass('java.lang.Object'), JClass('java.lang.Object')) + raise RuntimeError(f'Impossible state: missing case for {len(mapping_functions)}.') def flatten_last(self, flattening_function: Callable[[A], A_]) -> 'PythonUniConstraintStream[A_]': """Takes each tuple and applies a mapping on it, which turns the tuple into an Iterable. @@ -966,6 +1056,48 @@ def distinct(self) -> 'PythonUniConstraintStream[A]': """ return PythonUniConstraintStream(self.delegate.distinct(), self.package, self.a_type) + @overload + def concat(self, other: 'PythonUniConstraintStream[A]') -> 'PythonUniConstraintStream[A]': + ... + + @overload + def concat(self, other: 'PythonBiConstraintStream[A, B_]') -> 'PythonBiConstraintStream[A, B_]': + ... + + @overload + def concat(self, other: 'PythonTriConstraintStream[A, B_, C_]') -> 'PythonTriConstraintStream[A, B_, C_]': + ... + + @overload + def concat(self, other: 'PythonQuadConstraintStream[A, B_, C_, D_]') -> 'PythonQuadConstraintStream[A, B_, C_, D_]': + ... + + def concat(self, other): + """ + The concat building block allows you + to create a constraint stream containing tuples of two other constraint streams. + If join acts like a cartesian product of two lists, concat acts like a concatenation of two lists. + Unlike union of sets, concatenation of lists repeats duplicated elements. + If the two constraint concatenating streams share tuples, which happens e.g. + when they come from the same source of data, the tuples will be repeated downstream. + If this is undesired, use the distinct building block. + :param other: + :return: + """ + if isinstance(other, PythonUniConstraintStream): + return PythonUniConstraintStream(self.delegate.concat(other.delegate), self.package, self.a_type) + elif isinstance(other, PythonBiConstraintStream): + return PythonBiConstraintStream(self.delegate.concat(other.delegate), self.package, self.a_type, + other.b_type) + elif isinstance(other, PythonTriConstraintStream): + return PythonTriConstraintStream(self.delegate.concat(other.delegate), self.package, self.a_type, + other.b_type, other.c_type) + elif isinstance(other, PythonQuadConstraintStream): + return PythonQuadConstraintStream(self.delegate.concat(other.delegate), self.package, self.a_type, + other.b_type, other.c_type, other.d_type) + else: + raise RuntimeError(f'Unhandled constraint stream type {type(other)}.') + @overload def penalize(self, constraint_name: str, constraint_weight: 'Score') -> \ 'Constraint': @@ -1448,15 +1580,83 @@ def group_by(self, *args): groupBy = group_by - def map(self, mapping_function: Callable[[A,B],A_]) -> 'PythonUniConstraintStream[A_]': + @overload + def map(self, mapping_function: Callable[[A, B], A_]) -> 'PythonUniConstraintStream[A_]': + ... + + @overload + def map(self, mapping_function: Callable[[A, B], A_], mapping_function2: Callable[[A, B], B_]) -> 'PythonBiConstraintStream[A_, B_]': + ... + + @overload + def map(self, mapping_function: Callable[[A, B], A_], mapping_function2: Callable[[A, B], B_], + mapping_function3: Callable[[A, B], C_]) -> 'PythonTriConstraintStream[A_, B_, C_]': + ... + + @overload + def map(self, mapping_function: Callable[[A, B], A_], mapping_function2: Callable[[A, B], B_], + mapping_function3: Callable[[A, B], C_], mapping_function4: Callable[[A, B], D_]) -> 'PythonQuadConstraintStream[A_, B_, C_, D_]': + ... + + def map(self, *mapping_functions): """Transforms the stream in such a way that tuples are remapped using the given function. - :param mapping_function: + :param mapping_functions: :return: """ - translated_function = function_cast(mapping_function, self.a_type, self.b_type) - return PythonUniConstraintStream(self.delegate.map(translated_function), self.package, JClass('java.lang.Object')) + if len(mapping_functions) == 0: + raise ValueError(f'At least one mapping function is required for map.') + if len(mapping_functions) > 4: + raise ValueError(f'At most four mapping functions can be passed to map (got {len(mapping_functions)}).') + translated_functions = tuple(map(lambda mapping_function: function_cast(mapping_function, self.a_type, + self.b_type), + mapping_functions)) + if len(mapping_functions) == 1: + return PythonUniConstraintStream(self.delegate.map(*translated_functions), self.package, + JClass('java.lang.Object')) + if len(mapping_functions) == 2: + return PythonBiConstraintStream(self.delegate.map(*translated_functions), self.package, + JClass('java.lang.Object'), JClass('java.lang.Object')) + if len(mapping_functions) == 3: + return PythonTriConstraintStream(self.delegate.map(*translated_functions), self.package, + JClass('java.lang.Object'), JClass('java.lang.Object'), JClass('java.lang.Object')) + if len(mapping_functions) == 4: + return PythonQuadConstraintStream(self.delegate.map(*translated_functions), self.package, + JClass('java.lang.Object'), JClass('java.lang.Object'), + JClass('java.lang.Object'), JClass('java.lang.Object')) + raise RuntimeError(f'Impossible state: missing case for {len(mapping_functions)}.') + + @overload + def expand(self, mapping_function: Callable[[A, B], C_]) -> 'PythonTriConstraintStream[A, B, C_]': + ... + + @overload + def expand(self, mapping_function: Callable[[A, B], C_], mapping_function2: Callable[[A, B], D_]) -> 'PythonQuadConstraintStream[A, B, C_, D_]': + ... + + def expand(self, *mapping_functions): + """ + Tuple expansion is a special case of tuple mapping + which only increases stream cardinality and can not introduce duplicate tuples. + It enables you to add extra facts to each tuple in a constraint stream by applying a mapping function to it. + This is useful in situations where an expensive computations needs to be cached for use later in the stream. + :param mapping_functions: + :return: + """ + if len(mapping_functions) == 0: + raise ValueError(f'At least one mapping function is required for expand.') + if len(mapping_functions) > 2: + raise ValueError(f'At most two mapping functions can be passed to expand on a BiStream (got {len(mapping_functions)}).') + translated_functions = tuple(map(lambda mapping_function: function_cast(mapping_function, self.a_type, self.b_type), + mapping_functions)) + if len(mapping_functions) == 1: + return PythonTriConstraintStream(self.delegate.expand(*translated_functions), self.package, + self.a_type, self.b_type, JClass('java.lang.Object')) + if len(mapping_functions) == 2: + return PythonQuadConstraintStream(self.delegate.expand(*translated_functions), self.package, + self.a_type, self.b_type, JClass('java.lang.Object'), JClass('java.lang.Object')) + raise RuntimeError(f'Impossible state: missing case for {len(mapping_functions)}.') def flatten_last(self, flattening_function: Callable[[B], B_]) -> 'PythonBiConstraintStream[A,B_]': """Takes each tuple and applies a mapping on it, which turns the tuple into an Iterable. @@ -1478,6 +1678,48 @@ def distinct(self) -> 'PythonBiConstraintStream[A,B]': """ return PythonBiConstraintStream(self.delegate.distinct(), self.package, self.a_type, self.b_type) + @overload + def concat(self, other: 'PythonUniConstraintStream[A]') -> 'PythonBiConstraintStream[A, B]': + ... + + @overload + def concat(self, other: 'PythonBiConstraintStream[A, B]') -> 'PythonBiConstraintStream[A, B]': + ... + + @overload + def concat(self, other: 'PythonTriConstraintStream[A, B, C_]') -> 'PythonTriConstraintStream[A, B, C_]': + ... + + @overload + def concat(self, other: 'PythonQuadConstraintStream[A, B, C_, D_]') -> 'PythonQuadConstraintStream[A, B, C_, D_]': + ... + + def concat(self, other): + """ + The concat building block allows you + to create a constraint stream containing tuples of two other constraint streams. + If join acts like a cartesian product of two lists, concat acts like a concatenation of two lists. + Unlike union of sets, concatenation of lists repeats duplicated elements. + If the two constraint concatenating streams share tuples, which happens e.g. + when they come from the same source of data, the tuples will be repeated downstream. + If this is undesired, use the distinct building block. + :param other: + :return: + """ + if isinstance(other, PythonUniConstraintStream): + return PythonBiConstraintStream(self.delegate.concat(other.delegate), self.package, self.a_type, self.b_type) + elif isinstance(other, PythonBiConstraintStream): + return PythonBiConstraintStream(self.delegate.concat(other.delegate), self.package, self.a_type, + self.b_type) + elif isinstance(other, PythonTriConstraintStream): + return PythonTriConstraintStream(self.delegate.concat(other.delegate), self.package, self.a_type, + self.b_type, other.c_type) + elif isinstance(other, PythonQuadConstraintStream): + return PythonQuadConstraintStream(self.delegate.concat(other.delegate), self.package, self.a_type, + self.b_type, other.c_type, other.d_type) + else: + raise RuntimeError(f'Unhandled constraint stream type {type(other)}.') + @overload def penalize(self, constraint_name: str, constraint_weight: 'Score') -> \ 'Constraint': @@ -1975,16 +2217,66 @@ def group_by(self, *args): groupBy = group_by - def map(self, mapping_function: Callable[[A,B,C], A_]) -> 'PythonUniConstraintStream[A_]': + @overload + def map(self, mapping_function: Callable[[A, B, C], A_]) -> 'PythonUniConstraintStream[A_]': + ... + + @overload + def map(self, mapping_function: Callable[[A, B, C], A_], mapping_function2: Callable[[A, B, C], B_]) -> 'PythonBiConstraintStream[A_, B_]': + ... + + @overload + def map(self, mapping_function: Callable[[A, B, C], A_], mapping_function2: Callable[[A, B, C], B_], + mapping_function3: Callable[[A, B, C], C_]) -> 'PythonTriConstraintStream[A_, B_, C_]': + ... + + @overload + def map(self, mapping_function: Callable[[A, B, C], A_], mapping_function2: Callable[[A, B, C], B_], + mapping_function3: Callable[[A, B, C], C_], mapping_function4: Callable[[A, B, C], D_]) -> 'PythonQuadConstraintStream[A_, B_, C_, D_]': + ... + + def map(self, *mapping_functions): """Transforms the stream in such a way that tuples are remapped using the given function. - :param mapping_function: + :param mapping_functions: + :return: + """ + if len(mapping_functions) == 0: + raise ValueError(f'At least one mapping function is required for map.') + if len(mapping_functions) > 4: + raise ValueError(f'At most four mapping functions can be passed to map (got {len(mapping_functions)}).') + translated_functions = tuple(map(lambda mapping_function: function_cast(mapping_function, self.a_type, + self.b_type, self.c_type), + mapping_functions)) + if len(mapping_functions) == 1: + return PythonUniConstraintStream(self.delegate.map(*translated_functions), self.package, + JClass('java.lang.Object')) + if len(mapping_functions) == 2: + return PythonBiConstraintStream(self.delegate.map(*translated_functions), self.package, + JClass('java.lang.Object'), JClass('java.lang.Object')) + if len(mapping_functions) == 3: + return PythonTriConstraintStream(self.delegate.map(*translated_functions), self.package, + JClass('java.lang.Object'), JClass('java.lang.Object'), + JClass('java.lang.Object')) + if len(mapping_functions) == 4: + return PythonQuadConstraintStream(self.delegate.map(*translated_functions), self.package, + JClass('java.lang.Object'), JClass('java.lang.Object'), + JClass('java.lang.Object'), JClass('java.lang.Object')) + raise RuntimeError(f'Impossible state: missing case for {len(mapping_functions)}.') + + def expand(self, mapping_function: Callable[[A, B, C], D_]) -> 'QuadConstraintStream[A, B, C, D_]': + """ + Tuple expansion is a special case of tuple mapping + which only increases stream cardinality and can not introduce duplicate tuples. + It enables you to add extra facts to each tuple in a constraint stream by applying a mapping function to it. + This is useful in situations where an expensive computations needs to be cached for use later in the stream. + :param mapping_function: :return: """ translated_function = function_cast(mapping_function, self.a_type, self.b_type, self.c_type) - return PythonUniConstraintStream(self.delegate.map(translated_function), self.package, - JClass('java.lang.Object')) + return PythonTriConstraintStream(self.delegate.expand(translated_function), self.package, + self.a_type, self.b_type, self.c_type, JClass('java.lang.Object')) def flatten_last(self, flattening_function: Callable[[C], C_]) -> 'PythonTriConstraintStream[A,B,C_]': """Takes each tuple and applies a mapping on it, which turns the tuple into an Iterable. @@ -2007,6 +2299,49 @@ def distinct(self) -> 'PythonTriConstraintStream[A, B, C]': return PythonTriConstraintStream(self.delegate.distinct(), self.package, self.a_type, self.b_type, self.c_type) + @overload + def concat(self, other: 'PythonUniConstraintStream[A]') -> 'PythonTriConstraintStream[A, B, C]': + ... + + @overload + def concat(self, other: 'PythonBiConstraintStream[A, B]') -> 'PythonTriConstraintStream[A, B, C]': + ... + + @overload + def concat(self, other: 'PythonTriConstraintStream[A, B, C]') -> 'PythonTriConstraintStream[A, B, C]': + ... + + @overload + def concat(self, other: 'PythonQuadConstraintStream[A, B, C, D_]') -> 'PythonQuadConstraintStream[A, B, C, D_]': + ... + + def concat(self, other): + """ + The concat building block allows you + to create a constraint stream containing tuples of two other constraint streams. + If join acts like a cartesian product of two lists, concat acts like a concatenation of two lists. + Unlike union of sets, concatenation of lists repeats duplicated elements. + If the two constraint concatenating streams share tuples, which happens e.g. + when they come from the same source of data, the tuples will be repeated downstream. + If this is undesired, use the distinct building block. + :param other: + :return: + """ + if isinstance(other, PythonUniConstraintStream): + return PythonTriConstraintStream(self.delegate.concat(other.delegate), self.package, self.a_type, + self.b_type, self.c_type) + elif isinstance(other, PythonBiConstraintStream): + return PythonTriConstraintStream(self.delegate.concat(other.delegate), self.package, self.a_type, + self.b_type, self.c_type) + elif isinstance(other, PythonTriConstraintStream): + return PythonTriConstraintStream(self.delegate.concat(other.delegate), self.package, self.a_type, + self.b_type, self.c_type) + elif isinstance(other, PythonQuadConstraintStream): + return PythonQuadConstraintStream(self.delegate.concat(other.delegate), self.package, self.a_type, + self.b_type, self.c_type, other.d_type) + else: + raise RuntimeError(f'Unhandled constraint stream type {type(other)}.') + @overload def penalize(self, constraint_name: str, constraint_weight: 'Score') -> \ 'Constraint': @@ -2494,15 +2829,53 @@ def group_by(self, *args): groupBy = group_by - def map(self, mapping_function: Callable[[A,B,C,D], A_]) -> 'PythonUniConstraintStream[A_]': + @overload + def map(self, mapping_function: Callable[[A, B, C, D], A_]) -> 'PythonUniConstraintStream[A_]': + ... + + @overload + def map(self, mapping_function: Callable[[A, B, C, D], A_], mapping_function2: Callable[[A, B, C, D], B_]) -> 'PythonBiConstraintStream[A_, B_]': + ... + + @overload + def map(self, mapping_function: Callable[[A, B, C, D], A_], mapping_function2: Callable[[A, B, C, D], B_], + mapping_function3: Callable[[A, B, C, D], C_]) -> 'PythonTriConstraintStream[A_, B_, C_]': + ... + + @overload + def map(self, mapping_function: Callable[[A, B, C, D], A_], mapping_function2: Callable[[A, B, C, D], B_], + mapping_function3: Callable[[A, B, C, D], C_], mapping_function4: Callable[[A, B, C, D], D_]) -> 'PythonQuadConstraintStream[A_, B_, C_, D_]': + ... + + def map(self, *mapping_functions): """Transforms the stream in such a way that tuples are remapped using the given function. - :param mapping_function: + + :param mapping_functions: :return: """ - translated_function = function_cast(mapping_function, self.a_type, self.b_type, self.c_type, self.d_type) - return PythonUniConstraintStream(self.delegate.map(translated_function), self.package, - JClass('java.lang.Object')) + if len(mapping_functions) == 0: + raise ValueError(f'At least one mapping function is required for map.') + if len(mapping_functions) > 4: + raise ValueError(f'At most four mapping functions can be passed to map (got {len(mapping_functions)}).') + translated_functions = tuple(map(lambda mapping_function: function_cast(mapping_function, self.a_type, + self.b_type, self.c_type, self.d_type), + mapping_functions)) + if len(mapping_functions) == 1: + return PythonUniConstraintStream(self.delegate.map(*translated_functions), self.package, + JClass('java.lang.Object')) + if len(mapping_functions) == 2: + return PythonBiConstraintStream(self.delegate.map(*translated_functions), self.package, + JClass('java.lang.Object'), JClass('java.lang.Object')) + if len(mapping_functions) == 3: + return PythonTriConstraintStream(self.delegate.map(*translated_functions), self.package, + JClass('java.lang.Object'), JClass('java.lang.Object'), + JClass('java.lang.Object')) + if len(mapping_functions) == 4: + return PythonQuadConstraintStream(self.delegate.map(*translated_functions), self.package, + JClass('java.lang.Object'), JClass('java.lang.Object'), + JClass('java.lang.Object'), JClass('java.lang.Object')) + raise RuntimeError(f'Impossible state: missing case for {len(mapping_functions)}.') def flatten_last(self, flattening_function) -> 'PythonQuadConstraintStream[A,B,C,D]': """Takes each tuple and applies a mapping on it, which turns the tuple into an Iterable. @@ -2525,6 +2898,49 @@ def distinct(self) -> 'PythonQuadConstraintStream[A,B,C,D]': return PythonQuadConstraintStream(self.delegate.distinct(), self.package, self.a_type, self.b_type, self.c_type, self.d_type) + @overload + def concat(self, other: 'PythonUniConstraintStream[A]') -> 'PythonQuadConstraintStream[A, B, C, D]': + ... + + @overload + def concat(self, other: 'PythonBiConstraintStream[A, B]') -> 'PythonQuadConstraintStream[A, B, C, D]': + ... + + @overload + def concat(self, other: 'PythonTriConstraintStream[A, B, C]') -> 'PythonQuadConstraintStream[A, B, C, D]': + ... + + @overload + def concat(self, other: 'PythonQuadConstraintStream[A, B, C, D]') -> 'PythonQuadConstraintStream[A, B, C, D]': + ... + + def concat(self, other): + """ + The concat building block allows you + to create a constraint stream containing tuples of two other constraint streams. + If join acts like a cartesian product of two lists, concat acts like a concatenation of two lists. + Unlike union of sets, concatenation of lists repeats duplicated elements. + If the two constraint concatenating streams share tuples, which happens e.g. + when they come from the same source of data, the tuples will be repeated downstream. + If this is undesired, use the distinct building block. + :param other: + :return: + """ + if isinstance(other, PythonUniConstraintStream): + return PythonQuadConstraintStream(self.delegate.concat(other.delegate), self.package, self.a_type, + self.b_type, self.c_type, self.d_type) + elif isinstance(other, PythonBiConstraintStream): + return PythonQuadConstraintStream(self.delegate.concat(other.delegate), self.package, self.a_type, + self.b_type, self.c_type, self.d_type) + elif isinstance(other, PythonTriConstraintStream): + return PythonQuadConstraintStream(self.delegate.concat(other.delegate), self.package, self.a_type, + self.b_type, self.c_type, self.d_type) + elif isinstance(other, PythonQuadConstraintStream): + return PythonQuadConstraintStream(self.delegate.concat(other.delegate), self.package, self.a_type, + self.b_type, self.c_type, self.d_type) + else: + raise RuntimeError(f'Unhandled constraint stream type {type(other)}.') + @overload def penalize(self, constraint_name: str, constraint_weight: 'Score') -> \ 'Constraint': diff --git a/tox.ini b/tox.ini index 9256222a..84c7bda1 100644 --- a/tox.ini +++ b/tox.ini @@ -4,9 +4,10 @@ # and then run "tox" from this directory. [tox] -envlist = py39,py310,py311 +env_list = py39,py310,py311 [testenv] +pass_env = * # needed by tox4, to pass JAVA_HOME deps = pytest>=8.0.2 pytest-cov>=4.1.0