diff --git a/src/Fable.Cli/CHANGELOG.md b/src/Fable.Cli/CHANGELOG.md index 6509e4670..c2fdaa8bf 100644 --- a/src/Fable.Cli/CHANGELOG.md +++ b/src/Fable.Cli/CHANGELOG.md @@ -19,6 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +* [Python] Fix curry/uncurry to handle arbitrary number of arguments (by @dbrattli) * [Python] Fix type annotations for protocols, Option casting, and abstract classes (by @dbrattli) * [Python] Fix type annotations for curried functions and numeric types (by @dbrattli) * [Python] Fix type annotations for inref, IList, DateKind, and regex collections (by @dbrattli) diff --git a/src/Fable.Compiler/CHANGELOG.md b/src/Fable.Compiler/CHANGELOG.md index f8c79cfa5..ff54ef3a2 100644 --- a/src/Fable.Compiler/CHANGELOG.md +++ b/src/Fable.Compiler/CHANGELOG.md @@ -19,6 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +* [Python] Fix curry/uncurry to handle arbitrary number of arguments (by @dbrattli) * [Python] Fix type annotations for protocols, Option casting, and abstract classes (by @dbrattli) * [Python] Fix type annotations for curried functions and numeric types (by @dbrattli) * [Python] Fix type annotations for inref, IList, DateKind, and regex collections (by @dbrattli) diff --git a/src/Fable.Transforms/Python/Fable2Python.Annotation.fs b/src/Fable.Transforms/Python/Fable2Python.Annotation.fs index e01b3fdd9..80029675d 100644 --- a/src/Fable.Transforms/Python/Fable2Python.Annotation.fs +++ b/src/Fable.Transforms/Python/Fable2Python.Annotation.fs @@ -451,7 +451,7 @@ let makeNumberTypeAnnotation com ctx kind info = match name with | "int" | "float" -> Expression.name name - | _ -> fableModuleAnnotation com ctx "types" name [] + | _ -> fableModuleAnnotation com ctx "core" name [] match kind, info with @@ -631,7 +631,7 @@ let makeBuiltinTypeAnnotation com ctx typ repeatedGenerics kind = | Replacements.Util.BclGuid -> stdlibModuleTypeHint com ctx "uuid" "UUID" [] repeatedGenerics | Replacements.Util.FSharpReference genArg -> let resolved, stmts = resolveGenerics com ctx [ genArg ] repeatedGenerics - fableModuleAnnotation com ctx "types" "FSharpRef" resolved, stmts + fableModuleAnnotation com ctx "core" "FSharpRef" resolved, stmts (* | Replacements.Util.BclTimeSpan -> NumberTypeAnnotation | Replacements.Util.BclDateTime -> makeSimpleTypeAnnotation com ctx "Date" diff --git a/src/Fable.Transforms/Python/Fable2Python.Reflection.fs b/src/Fable.Transforms/Python/Fable2Python.Reflection.fs index 312740858..dd860b754 100644 --- a/src/Fable.Transforms/Python/Fable2Python.Reflection.fs +++ b/src/Fable.Transforms/Python/Fable2Python.Reflection.fs @@ -17,7 +17,7 @@ let private libReflectionCall (com: IPythonCompiler) ctx r memberName args = /// Wraps a Python list expression in Array(...) constructor let private arrayExpr (com: IPythonCompiler) ctx (items: Expression list) = - Expression.call (libValue com ctx "types" "Array", [ Expression.list items ]) + Expression.call (libValue com ctx "array_" "Array", [ Expression.list items ]) let private transformRecordReflectionInfo com ctx r (ent: Fable.Entity) generics = // TODO: Refactor these three bindings to reuse in transformUnionReflectionInfo @@ -318,18 +318,18 @@ let transformTypeTest (com: IPythonCompiler) ctx range expr (typ: Fable.Type) : | Fable.String -> pyTypeof "" expr | Fable.Number(kind, _b) -> match kind, typ with - | _, Fable.Type.Number(UInt8, _) -> pyInstanceof (libValue com ctx "types" "uint8") expr - | _, Fable.Type.Number(Int8, _) -> pyInstanceof (libValue com ctx "types" "int8") expr - | _, Fable.Type.Number(Int16, _) -> pyInstanceof (libValue com ctx "types" "int16") expr - | _, Fable.Type.Number(UInt16, _) -> pyInstanceof (libValue com ctx "types" "uint16") expr - | _, Fable.Type.Number(Int32, _) -> pyInstanceof (libValue com ctx "types" "int32") expr - | _, Fable.Type.Number(UInt32, _) -> pyInstanceof (libValue com ctx "types" "uint32") expr + | _, Fable.Type.Number(UInt8, _) -> pyInstanceof (libValue com ctx "core" "uint8") expr + | _, Fable.Type.Number(Int8, _) -> pyInstanceof (libValue com ctx "core" "int8") expr + | _, Fable.Type.Number(Int16, _) -> pyInstanceof (libValue com ctx "core" "int16") expr + | _, Fable.Type.Number(UInt16, _) -> pyInstanceof (libValue com ctx "core" "uint16") expr + | _, Fable.Type.Number(Int32, _) -> pyInstanceof (libValue com ctx "core" "int32") expr + | _, Fable.Type.Number(UInt32, _) -> pyInstanceof (libValue com ctx "core" "uint32") expr | _, Fable.Type.Number(NativeInt, _) | _, Fable.Type.Number(UNativeInt, _) -> pyInstanceof (Expression.name "int") expr - | _, Fable.Type.Number(Int64, _) -> pyInstanceof (libValue com ctx "types" "int64") expr - | _, Fable.Type.Number(UInt64, _) -> pyInstanceof (libValue com ctx "types" "uint64") expr - | _, Fable.Type.Number(Float32, _) -> pyInstanceof (libValue com ctx "types" "float32") expr - | _, Fable.Type.Number(Float64, _) -> pyInstanceof (libValue com ctx "types" "float64") expr + | _, Fable.Type.Number(Int64, _) -> pyInstanceof (libValue com ctx "core" "int64") expr + | _, Fable.Type.Number(UInt64, _) -> pyInstanceof (libValue com ctx "core" "uint64") expr + | _, Fable.Type.Number(Float32, _) -> pyInstanceof (libValue com ctx "core" "float32") expr + | _, Fable.Type.Number(Float64, _) -> pyInstanceof (libValue com ctx "core" "float64") expr | _, Fable.Type.Number(Decimal, _) -> pyTypeof "" expr | _ -> pyInstanceof (Expression.name "int") expr @@ -338,7 +338,7 @@ let transformTypeTest (com: IPythonCompiler) ctx range expr (typ: Fable.Type) : | Fable.DelegateType _ -> pyTypeof "" expr | Fable.Array _ -> // Use isinstance(x, Array) where Array is from fable_library.types - pyInstanceof (libValue com ctx "types" "Array") expr + pyInstanceof (libValue com ctx "array_" "Array") expr | Fable.Tuple _ -> // Use isinstance(x, tuple) for Python tuple type test pyInstanceof (Expression.name "tuple") expr @@ -366,10 +366,10 @@ let transformTypeTest (com: IPythonCompiler) ctx range expr (typ: Fable.Type) : [ expr ] |> libCall com ctx None "util" "isIterable", stmts | Types.array -> // Use isinstance(x, Array) where Array is from fable_library.types - pyInstanceof (libValue com ctx "types" "Array") expr + pyInstanceof (libValue com ctx "array_" "Array") expr | Types.exception_ -> let expr, stmts = com.TransformAsExpr(ctx, expr) - [ expr ] |> libCall com ctx None "types" "isException", stmts + [ expr ] |> libCall com ctx None "exceptions" "is_exception", stmts | Types.datetime -> pyInstanceof (com.GetImportExpr(ctx, "datetime", "datetime")) expr | _ -> let ent = com.GetEntity(ent) diff --git a/src/Fable.Transforms/Python/Fable2Python.Transforms.fs b/src/Fable.Transforms/Python/Fable2Python.Transforms.fs index d4af3bebc..7d5d855c0 100644 --- a/src/Fable.Transforms/Python/Fable2Python.Transforms.fs +++ b/src/Fable.Transforms/Python/Fable2Python.Transforms.fs @@ -111,9 +111,9 @@ let makeArrowFunctionExpression let args = match args.PosOnlyArgs, args.Args with | [], [] -> - let ta = com.GetImportExpr(ctx, "typing", "Any") + let ta = com.GetImportExpr(ctx, getLibPath com "util", "Unit") - Arguments.arguments (args = [ Arg.arg ("__unit", annotation = ta) ], defaults = [ Expression.none ]) + Arguments.arguments (args = [ Arg.arg ("__unit", annotation = ta) ], defaults = [ Expression.tuple [] ]) | _ -> args let allDefaultsAreNone = @@ -320,15 +320,15 @@ let transformCast (com: IPythonCompiler) (ctx: Context) t e : Expression * State | _ -> com.TransformAsExpr(ctx, e) | Fable.Number(Float32, _), _ -> - let cons = libValue com ctx "types" "float32" + let cons = libValue com ctx "core" "float32" let value, stmts = com.TransformAsExpr(ctx, e) Expression.call (cons, [ value ], ?loc = None), stmts | Fable.Number(Float64, _), _ -> - let cons = libValue com ctx "types" "float64" + let cons = libValue com ctx "core" "float64" let value, stmts = com.TransformAsExpr(ctx, e) Expression.call (cons, [ value ], ?loc = None), stmts | Fable.Number(Int32, _), _ -> - let cons = libValue com ctx "types" "int32" + let cons = libValue com ctx "core" "int32" let value, stmts = com.TransformAsExpr(ctx, e) Expression.call (cons, [ value ], ?loc = None), stmts | _ -> com.TransformAsExpr(ctx, e) @@ -393,9 +393,9 @@ let transformValue (com: IPythonCompiler) (ctx: Context) r value : Expression * | Fable.NumberValue.Float64 x when x = -infinity -> libValue com ctx "double" "float64.negative_infinity", [] | Fable.NumberValue.Float64 x when Double.IsNaN(x) -> libValue com ctx "double" "float64.nan", [] | Fable.NumberValue.Float32 x when Single.IsNaN(x) -> - libCall com ctx r "types" "float32" [ Expression.stringConstant "nan" ], [] + libCall com ctx r "core" "float32" [ Expression.stringConstant "nan" ], [] | Fable.NumberValue.Float16 x when Single.IsNaN(x) -> - libCall com ctx r "types" "float32" [ Expression.stringConstant "nan" ], [] + libCall com ctx r "core" "float32" [ Expression.stringConstant "nan" ], [] | Fable.NumberValue.Float16 x -> makeFloat com ctx r value.Type "float32" (float x) | Fable.NumberValue.Float32 x -> makeFloat com ctx r value.Type "float32" (float x) | Fable.NumberValue.Float64 x -> makeFloat com ctx r value.Type "float64" (float x) @@ -2977,21 +2977,23 @@ let transformFunction args', finalDefaults, body let arguments = + let unitDefault = libValue com ctx "util" "UNIT" + match args, isUnitOrGeneric, tcArgs with // No args and no tail-call args: add __unit parameter | [], _, [] -> - let unitDefault = libValue com ctx "util" "UNIT" - Arguments.arguments (args = [ Arg.arg (Identifier("__unit")) ], defaults = [ unitDefault ]) + let unitType = com.GetImportExpr(ctx, getLibPath com "util", "Unit") + + Arguments.arguments ( + args = [ Arg.arg (Identifier("__unit"), annotation = unitType) ], + defaults = [ unitDefault ] + ) // No args but has tail-call args: skip __unit, tcArgs are sufficient | [], _, _ -> Arguments.arguments (args = tcArgs, defaults = tcDefaults) - // Single generic/unit arg with no tail-call args: keep it with UNIT default - | [ arg ], true, [] -> - let unitDefault = libValue com ctx "util" "UNIT" - Arguments.arguments (args = args, defaults = [ unitDefault ]) + // Single generic/unit arg with no tail-call args: keep it with () default + | [ arg ], true, [] -> Arguments.arguments (args = args, defaults = [ unitDefault ]) // Single generic/unit arg with tail-call args: keep arg (body may reference it) - | [ arg ], true, _ -> - let unitDefault = libValue com ctx "util" "UNIT" - Arguments.arguments (args @ tcArgs, defaults = unitDefault :: tcDefaults) + | [ arg ], true, _ -> Arguments.arguments (args @ tcArgs, defaults = unitDefault :: tcDefaults) | _ -> Arguments.arguments (args @ tcArgs, defaults = defaults @ tcDefaults) arguments, body @@ -3828,7 +3830,7 @@ let transformUnion (com: IPythonCompiler) ctx (ent: Fable.Entity) (entName: stri decoratorList = decorators ) - let baseExpr = libValue com ctx "types" "Union" |> Some + let baseExpr = libValue com ctx "union" "Union" |> Some let classMembers = List.append [ cases ] classMembers declareType com ctx ent entName args isOptional body baseExpr classMembers None [] [] @@ -3853,9 +3855,9 @@ let transformClassWithCompilerGeneratedConstructor let baseExpr = if ent.IsFSharpExceptionDeclaration then - libValue com ctx "types" "FSharpException" |> Some + libValue com ctx "exceptions" "FSharpException" |> Some elif ent.IsFSharpRecord || ent.IsValueType then - libValue com ctx "types" "Record" |> Some + libValue com ctx "record" "Record" |> Some else None @@ -4055,7 +4057,7 @@ let transformClassWithPrimaryConstructor |> extractBaseExprFromBaseCall com ctx classEnt.BaseType |> Option.orElseWith (fun () -> if classEnt.IsValueType then - Some(libValue com ctx "Types" "Record", ([], [], [])) + Some(libValue com ctx "record" "Record", ([], [], [])) else None ) diff --git a/src/Fable.Transforms/Python/Fable2Python.Util.fs b/src/Fable.Transforms/Python/Fable2Python.Util.fs index 79233903f..ccb594519 100644 --- a/src/Fable.Transforms/Python/Fable2Python.Util.fs +++ b/src/Fable.Transforms/Python/Fable2Python.Util.fs @@ -489,7 +489,7 @@ module Util = let ofInt (com: IPythonCompiler) (ctx: Context) (i: int) = //Expression.intConstant (int i) - libCall com ctx None "types" "int32" [ Expression.intConstant (int i) ] + libCall com ctx None "core" "int32" [ Expression.intConstant (int i) ] let ofString (s: string) = Expression.stringConstant s @@ -560,7 +560,7 @@ module Util = if l = "Any" then com.GetImportExpr(ctx, "typing", "Any") else - libValue com ctx "types" l + libValue com ctx "core" l let types_array = Expression.subscript (value = array, slice = type_obj, ctx = Load) Expression.call (types_array, [ expr ]) @@ -803,7 +803,7 @@ module Util = Expression.attribute (expr, Identifier field, ctx = Load), [] let makeInteger (com: IPythonCompiler) (ctx: Context) r _t intName (x: obj) = - let cons = libValue com ctx "types" intName + let cons = libValue com ctx "core" intName let value = Expression.intConstant (x, ?loc = r) // Added support for a few selected literals for performance reasons @@ -927,7 +927,7 @@ module Util = | _ -> Expression.call (cons, [ value ], ?loc = r), [] let makeFloat (com: IPythonCompiler) (ctx: Context) r _t floatName x = - let cons = libValue com ctx "types" floatName + let cons = libValue com ctx "core" floatName let value = Expression.floatConstant (x, ?loc = r) Expression.call (cons, [ value ], ?loc = r), [] diff --git a/src/Fable.Transforms/Python/Replacements.fs b/src/Fable.Transforms/Python/Replacements.fs index e04c1165f..c112a2436 100644 --- a/src/Fable.Transforms/Python/Replacements.fs +++ b/src/Fable.Transforms/Python/Replacements.fs @@ -86,7 +86,7 @@ let coreModFor = | FSharpMap _ -> "map" | FSharpResult _ -> "result" | FSharpChoice _ -> "choice" - | FSharpReference _ -> "types" + | FSharpReference _ -> "core" | BclHashSet _ -> "mutable_set" | BclDictionary _ -> "mutable_map" | BclKeyValuePair _ @@ -120,7 +120,7 @@ let setRefCell com r (expr: Expr) (value: Expr) = let makeRefCell com r genArg args = let typ = makeFSharpCoreType [ genArg ] Types.refCell - Helper.LibCall(com, "types", "FSharpRef", typ, args, isConstructor = true, ?loc = r) + Helper.LibCall(com, "core", "FSharpRef", typ, args, isConstructor = true, ?loc = r) let makeRefCellFromValue com r (value: Expr) = let typ = value.Type @@ -300,7 +300,7 @@ let stringToInt com (_ctx: Context) r targetType (args: Expr list) : Expr = let style = int System.Globalization.NumberStyles.Any // Use the type's static parse method: e.g., int8.parse(string, style) let typeName = getIntTypeName kind - let typeExpr = Helper.LibValue(com, "types", typeName, Any) + let typeExpr = Helper.LibValue(com, "core", typeName, Any) Helper.InstanceCall(typeExpr, "parse", targetType, [ args.Head; makeIntConst style ] @ args.Tail, ?loc = r) let toLong com (ctx: Context) r (unsigned: bool) targetType (args: Expr list) : Expr = @@ -346,12 +346,12 @@ let toInt com (ctx: Context) r targetType (args: Expr list) = let emitCast typeTo arg = match typeTo with - | Int8 -> Helper.LibCall(com, "types", "sbyte", targetType, [ arg ]) - | Int16 -> Helper.LibCall(com, "types", "int16", targetType, [ arg ]) - | Int32 -> Helper.LibCall(com, "types", "int32", targetType, [ arg ]) - | UInt8 -> Helper.LibCall(com, "types", "byte", targetType, [ arg ]) - | UInt16 -> Helper.LibCall(com, "types", "uint16", targetType, [ arg ]) - | UInt32 -> Helper.LibCall(com, "types", "uint32", targetType, [ arg ]) + | Int8 -> Helper.LibCall(com, "core", "sbyte", targetType, [ arg ]) + | Int16 -> Helper.LibCall(com, "core", "int16", targetType, [ arg ]) + | Int32 -> Helper.LibCall(com, "core", "int32", targetType, [ arg ]) + | UInt8 -> Helper.LibCall(com, "core", "byte", targetType, [ arg ]) + | UInt16 -> Helper.LibCall(com, "core", "uint16", targetType, [ arg ]) + | UInt32 -> Helper.LibCall(com, "core", "uint32", targetType, [ arg ]) | _ -> // Use normal Python int for BigInt, NativeInt, UNativeInt Helper.GlobalCall("int", targetType, [ arg ]) @@ -366,7 +366,7 @@ let toInt com (ctx: Context) r targetType (args: Expr list) = if needToCast typeFrom typeTo then match typeFrom with | Int64 - | UInt64 -> Helper.LibCall(com, "types", "int32", targetType, args) + | UInt64 -> Helper.LibCall(com, "core", "int32", targetType, args) | Decimal -> Helper.LibCall(com, "Decimal", "to_int", targetType, args) | _ -> args.Head |> emitCast typeTo @@ -396,7 +396,7 @@ let toString com (ctx: Context) r (args: Expr list) = | Builtin BclGuid when tail.IsEmpty -> Helper.GlobalCall("str", String, [ head ], ?loc = r) | Builtin(BclGuid | BclTimeSpan as bt) -> Helper.LibCall(com, coreModFor bt, "to_string", String, args) | Number(Int32, _) -> - let expr = Helper.LibCall(com, "types", "int32", head.Type, [ head ], ?loc = r) + let expr = Helper.LibCall(com, "core", "int32", head.Type, [ head ], ?loc = r) Helper.InstanceCall(expr, "to_string", String, tail, ?loc = r) | Number((Int8 | UInt8 | UInt16 | Int16 | UInt32 | Int64 | UInt64), _) -> if tail.Length > 0 then @@ -405,13 +405,13 @@ let toString com (ctx: Context) r (args: Expr list) = Helper.GlobalCall("str", String, [ head ], ?loc = r) | Number(BigInt, _) -> Helper.LibCall(com, "util", "int_to_string", String, args) | Number(Decimal, _) -> Helper.LibCall(com, "decimal", "to_string", String, args) - | Number _ -> Helper.LibCall(com, "types", "to_string", String, [ head ], ?loc = r) + | Number _ -> Helper.LibCall(com, "exceptions", "to_string", String, [ head ], ?loc = r) | Array _ - | List _ -> Helper.LibCall(com, "types", "seqToString", String, [ head ], ?loc = r) + | List _ -> Helper.LibCall(com, "exceptions", "seq_to_string", String, [ head ], ?loc = r) // | DeclaredType(ent, _) when ent.IsFSharpUnion || ent.IsFSharpRecord || ent.IsValueType -> // Helper.InstanceCall(head, "toString", String, [], ?loc=r) // | DeclaredType(ent, _) -> - | _ -> Helper.LibCall(com, "types", "toString", String, [ head ], ?loc = r) + | _ -> Helper.LibCall(com, "exceptions", "to_string", String, [ head ], ?loc = r) let round com (args: Expr list) = match args.Head.Type with @@ -1569,7 +1569,7 @@ let stringModule (com: ICompiler) (ctx: Context) r t (i: CallInfo) (_: Expr opti | "Length", [ arg ] -> // Use int32(len()) to ensure consistent return type let lenExpr = Helper.GlobalCall("len", Int32.Number, [ arg ], ?loc = r) - Helper.LibCall(com, "types", "int32", t, [ lenExpr ], ?loc = r) |> Some + Helper.LibCall(com, "core", "int32", t, [ lenExpr ], ?loc = r) |> Some | ("Iterate" | "IterateIndexed" | "ForAll" | "Exists"), _ -> // Cast the string to char[], see #1279 let args = args |> List.replaceLast (fun e -> stringToCharArray e.Type e) @@ -1604,7 +1604,7 @@ let formattableString let lenExpr = Helper.GlobalCall("len", Int32.Number, [ getField x "args" ], ?loc = r) - Helper.LibCall(com, "types", "int32", t, [ lenExpr ], ?loc = r) |> Some + Helper.LibCall(com, "core", "int32", t, [ lenExpr ], ?loc = r) |> Some | "GetArgument", Some x, [ idx ] -> getExpr r t (getField x "args") idx |> Some | "GetArguments", Some x, [] -> getFieldWith r t x "args" |> Some | _ -> None @@ -1747,7 +1747,7 @@ let resizeArrays (com: ICompiler) (ctx: Context) r (t: Type) (i: CallInfo) (this // ResizeArray is Python list - use len() wrapped in int32() | Array(_, ResizeArray) -> let lenExpr = Helper.GlobalCall("len", Int32.Number, [ ar ], ?loc = r) - Helper.LibCall(com, "types", "int32", t, [ lenExpr ], ?loc = r) |> Some + Helper.LibCall(com, "core", "int32", t, [ lenExpr ], ?loc = r) |> Some // MutableArray/ImmutableArray are FSharpArray (Rust) with .length property returning Int32 | Array _ -> getFieldWith r t ar "length" |> Some | _ -> Helper.LibCall(com, "util", "count", t, [ ar ], ?loc = r) |> Some @@ -1934,7 +1934,7 @@ let arrayModule (com: ICompiler) (ctx: Context) r (t: Type) (i: CallInfo) (_: Ex // ResizeArray is Python list - use len() wrapped in int32() | Array(_, ResizeArray) -> let lenExpr = Helper.GlobalCall("len", Int32.Number, [ arg ], ?loc = r) - Helper.LibCall(com, "types", "int32", t, [ lenExpr ], ?loc = r) |> Some + Helper.LibCall(com, "core", "int32", t, [ lenExpr ], ?loc = r) |> Some // MutableArray/ImmutableArray are FSharpArray (Rust) with .length property returning Int32 | _ -> getFieldWith r t arg "length" |> Some | "Item", [ idx; ar ] -> getExpr r t ar idx |> Some @@ -2182,7 +2182,7 @@ let parseNum (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Expr op // For integer types, call the static parse method on the type // This generates: int8.parse(string, style) instead of parse_int32(string, style, unsigned, bitsize) let typeName = getIntTypeName kind - let typeExpr = Helper.LibValue(com, "types", typeName, Any) + let typeExpr = Helper.LibValue(com, "core", typeName, Any) let args = [ str; makeIntConst style ] @ outValue Helper.InstanceCall(typeExpr, Naming.lowerFirst meth, t, args, ?loc = r) |> Some @@ -2570,7 +2570,7 @@ let dictionaries (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Exp | "get_Count", Some c -> // Use int32(len()) to work with both Dictionary class and plain Python dict let lenExpr = Helper.GlobalCall("len", Int32.Number, [ c ], ?loc = r) - Helper.LibCall(com, "types", "int32", t, [ lenExpr ], ?loc = r) |> Some + Helper.LibCall(com, "core", "int32", t, [ lenExpr ], ?loc = r) |> Some | "GetEnumerator", Some callee -> getEnumerator com r t callee |> Some | "ContainsValue", _ -> match thisArg, args with @@ -3066,7 +3066,7 @@ let random (com: ICompiler) (ctx: Context) r t (i: CallInfo) (_: Expr option) (a |> Some | "NextDouble" -> let ranExpr = Helper.ImportedCall("random", "random", t, [], []) - Helper.LibCall(com, "types", "float64", t, [ ranExpr ], ?loc = r) |> Some + Helper.LibCall(com, "core", "float64", t, [ ranExpr ], ?loc = r) |> Some | "NextBytes" -> let byteArray = match args with @@ -3185,7 +3185,7 @@ let regex com (ctx: Context) r t (i: CallInfo) (thisArg: Expr option) (args: Exp thisArg |> Option.map (fun c -> let lenExpr = Helper.GlobalCall("len", Int32.Number, [ c ], ?loc = r) - Helper.LibCall(com, "types", "int32", t, [ lenExpr ], ?loc = r) + Helper.LibCall(com, "core", "int32", t, [ lenExpr ], ?loc = r) ) | "GetEnumerator" -> getEnumerator com r t thisArg.Value |> Some | "IsMatch" diff --git a/src/Fable.Transforms/Replacements.Util.fs b/src/Fable.Transforms/Replacements.Util.fs index 76af1b9bb..a2d937e0d 100644 --- a/src/Fable.Transforms/Replacements.Util.fs +++ b/src/Fable.Transforms/Replacements.Util.fs @@ -557,7 +557,10 @@ let partialApplyAtRuntime (com: Compiler) t arity (expr: Expr) (partialArgs: Exp let curriedType = makeLambdaType argTypes returnType let curried = - Helper.LibCall(com, "Util", $"curry{argTypes.Length}", curriedType, [ expr ]) + match com.Options.Language with + | Python -> + Helper.LibCall(com, "Curry", "curry", curriedType, [ makeNativeIntConst argTypes.Length; expr ]) + | _ -> Helper.LibCall(com, "Util", $"curry%d{argTypes.Length}", curriedType, [ expr ]) match partialArgs with | [] -> curried @@ -608,7 +611,9 @@ let uncurryExprAtRuntime (com: Compiler) arity (expr: Expr) = | Python -> let uncurriedType = DelegateType(argTypes, returnType) - Helper.LibCall(com, "Util", $"uncurry{arity}", uncurriedType, [ expr ]) + match com.Options.Language with + | Python -> Helper.LibCall(com, "Curry", "uncurry", uncurriedType, [ makeNativeIntConst arity; expr ]) + | _ -> Helper.LibCall(com, "Util", $"uncurry%d{arity}", uncurriedType, [ expr ]) | _ -> // let makeArgIdent typ = makeTypedIdent typ $"a{com.IncrementCounter()}$" // let argIdents = argTypes |> List.map makeArgIdent diff --git a/src/Fable.Transforms/Transforms.Util.fs b/src/Fable.Transforms/Transforms.Util.fs index 0c68d5efe..24f22862a 100644 --- a/src/Fable.Transforms/Transforms.Util.fs +++ b/src/Fable.Transforms/Transforms.Util.fs @@ -1046,6 +1046,10 @@ module AST = let makeIntConst (x: int) = NumberConstant(NumberValue.Int32 x, NumberInfo.Empty) |> makeValue None + let makeNativeIntConst (x: int) = + NumberConstant(NumberValue.NativeInt(nativeint x), NumberInfo.Empty) + |> makeValue None + let makeFloatConst (x: float) = NumberConstant(NumberValue.Float64 x, NumberInfo.Empty) |> makeValue None diff --git a/src/fable-library-py/fable_library/async_.py b/src/fable-library-py/fable_library/async_.py index 05aaacfa5..32ca38ea2 100644 --- a/src/fable-library-py/fable_library/async_.py +++ b/src/fable-library-py/fable_library/async_.py @@ -7,7 +7,6 @@ from threading import Timer from typing import ( Any, - Literal, ) from .array_ import Array @@ -34,6 +33,7 @@ from .protocols import IEnumerable_1 from .task import TaskCompletionSource from .time_span import TimeSpan, to_milliseconds +from .types import UNIT, Unit def cancellation_token() -> Async[CancellationToken]: @@ -96,7 +96,7 @@ def timeout(): def ignore(computation: Async[Any]) -> Async[None]: - def binder(_: Any | None = None) -> Async[None]: + def binder(_: Any | Unit = UNIT) -> Async[None]: return protected_return(None) return protected_bind(computation, binder) @@ -142,7 +142,7 @@ def _arrow19(_arg_1: T) -> Async[None]: return singleton.Bind(cmp, _arrow19) - def _arrow21(__unit: Literal[None] = None) -> Async[Array[T]]: + def _arrow21(__unit: Unit = UNIT) -> Async[Array[T]]: return singleton.Return(Array[T](results)) return singleton.Combine(singleton.For(computations, _arrow20), singleton.Delay(_arrow21)) diff --git a/src/fable-library-py/fable_library/big_int.py b/src/fable-library-py/fable_library/big_int.py index d63c010aa..de6b4d3ed 100644 --- a/src/fable-library-py/fable_library/big_int.py +++ b/src/fable-library-py/fable_library/big_int.py @@ -1,7 +1,7 @@ from decimal import Decimal from typing import Any, SupportsInt -from .types import FSharpRef +from .core import FSharpRef def compare(x: int, y: int) -> int: diff --git a/src/fable-library-py/fable_library/bit_converter.py b/src/fable-library-py/fable_library/bit_converter.py index 323d8c3c4..962c9a23b 100644 --- a/src/fable-library-py/fable_library/bit_converter.py +++ b/src/fable-library-py/fable_library/bit_converter.py @@ -3,7 +3,7 @@ from typing import SupportsFloat, SupportsInt from .array_ import Array -from .types import byte, float32, float64, int16, int32, int64, uint8, uint16, uint32, uint64 +from .core import byte, float32, float64, int16, int32, int64, uint8, uint16, uint32, uint64 def _bytes_to_uint8_array(data: bytes) -> Array[uint8]: diff --git a/src/fable-library-py/fable_library/boolean.py b/src/fable-library-py/fable_library/boolean.py index 778c666ff..e39670228 100644 --- a/src/fable-library-py/fable_library/boolean.py +++ b/src/fable-library-py/fable_library/boolean.py @@ -1,6 +1,6 @@ from typing import Any -from .types import FSharpRef +from .core import FSharpRef def try_parse(string: str, defValue: FSharpRef[bool]) -> bool: diff --git a/src/fable-library-py/fable_library/choice.pyi b/src/fable-library-py/fable_library/choice.pyi index 46a9e0677..10e26eec3 100644 --- a/src/fable-library-py/fable_library/choice.pyi +++ b/src/fable-library-py/fable_library/choice.pyi @@ -1,55 +1,45 @@ -from __future__ import annotations - from collections.abc import Callable -from typing import Any, Generic, TypeVar, overload +from typing import Any, overload from .reflection import TypeInfo -from .types import Union - -_T1 = TypeVar("_T1") -_T2 = TypeVar("_T2") -_T3 = TypeVar("_T3") -_T4 = TypeVar("_T4") -_T5 = TypeVar("_T5") -_T6 = TypeVar("_T6") -_T7 = TypeVar("_T7") +from .union import Union -class FSharpChoice_2(Union, Generic[_T1, _T2]): +class FSharpChoice_2[T1, T2](Union): def __init__(self, tag: int, *fields: Any) -> None: ... @staticmethod def cases() -> list[str]: ... FSharpChoice_2_reflection: Callable[[TypeInfo, TypeInfo], TypeInfo] -class FSharpChoice_3(Union, Generic[_T1, _T2, _T3]): +class FSharpChoice_3[T1, T2, T3](Union): def __init__(self, tag: int, *fields: Any) -> None: ... @staticmethod def cases() -> list[str]: ... FSharpChoice_3_reflection: Callable[[TypeInfo, TypeInfo, TypeInfo], TypeInfo] -class FSharpChoice_4(Union, Generic[_T1, _T2, _T3, _T4]): +class FSharpChoice_4[T1, T2, T3, T4](Union): def __init__(self, tag: int, *fields: Any) -> None: ... @staticmethod def cases() -> list[str]: ... FSharpChoice_4_reflection: Callable[[TypeInfo, TypeInfo, TypeInfo, TypeInfo], TypeInfo] -class FSharpChoice_5(Union, Generic[_T1, _T2, _T3, _T4, _T5]): +class FSharpChoice_5[T1, T2, T3, T4, T5](Union): def __init__(self, tag: int, *fields: Any) -> None: ... @staticmethod def cases() -> list[str]: ... FSharpChoice_5_reflection: Callable[[TypeInfo, TypeInfo, TypeInfo, TypeInfo, TypeInfo], TypeInfo] -class FSharpChoice_6(Union, Generic[_T1, _T2, _T3, _T4, _T5, _T6]): +class FSharpChoice_6[T1, T2, T3, T4, T5, T6](Union): def __init__(self, tag: int, *fields: Any) -> None: ... @staticmethod def cases() -> list[str]: ... FSharpChoice_6_reflection: Callable[[TypeInfo, TypeInfo, TypeInfo, TypeInfo, TypeInfo, TypeInfo], TypeInfo] -class FSharpChoice_7(Union, Generic[_T1, _T2, _T3, _T4, _T5, _T6, _T7]): +class FSharpChoice_7[T1, T2, T3, T4, T5, T6, T7](Union): def __init__(self, tag: int, *fields: Any) -> None: ... @staticmethod def cases() -> list[str]: ... @@ -59,13 +49,13 @@ FSharpChoice_7_reflection: Callable[[TypeInfo, TypeInfo, TypeInfo, TypeInfo, Typ @overload def Choice_makeChoice1Of2() -> FSharpChoice_2[None, Any]: ... @overload -def Choice_makeChoice1Of2(x: _T1) -> FSharpChoice_2[_T1, Any]: ... +def Choice_makeChoice1Of2[T1](x: T1) -> FSharpChoice_2[T1, Any]: ... @overload def Choice_makeChoice2Of2() -> FSharpChoice_2[Any, None]: ... @overload -def Choice_makeChoice2Of2(x: _T2) -> FSharpChoice_2[Any, _T2]: ... -def Choice_tryValueIfChoice1Of2(x: FSharpChoice_2[_T1, Any]) -> _T1 | None: ... -def Choice_tryValueIfChoice2Of2(x: FSharpChoice_2[Any, _T2]) -> _T2 | None: ... +def Choice_makeChoice2Of2[T2](x: T2) -> FSharpChoice_2[Any, T2]: ... +def Choice_tryValueIfChoice1Of2[T1](x: FSharpChoice_2[T1, Any]) -> T1 | None: ... +def Choice_tryValueIfChoice2Of2[T2](x: FSharpChoice_2[Any, T2]) -> T2 | None: ... __all__ = [ "Choice_makeChoice1Of2", diff --git a/src/fable-library-py/fable_library/core/array.pyi b/src/fable-library-py/fable_library/core/array.pyi index 26037725d..f84e9027e 100644 --- a/src/fable-library-py/fable_library/core/array.pyi +++ b/src/fable-library-py/fable_library/core/array.pyi @@ -18,6 +18,7 @@ from fable_library.protocols import ( IGenericAdder, IGenericAverager, ) +from fable_library.util import UNIT, Unit from . import FSharpRef, Int32 @@ -60,7 +61,7 @@ class FSharpArray[T](MutableSequence[T]): def length(self) -> Int32: ... # IEnumerable implementation (for .NET compatibility) - def GetEnumerator(self, __unit: None = None) -> IEnumerator[T]: ... + def GetEnumerator(self, __unit: Unit = UNIT) -> IEnumerator[T]: ... # Static methods @staticmethod diff --git a/src/fable-library-py/fable_library/curry.py b/src/fable-library-py/fable_library/curry.py new file mode 100644 index 000000000..d87b3fbb8 --- /dev/null +++ b/src/fable-library-py/fable_library/curry.py @@ -0,0 +1,108 @@ +from __future__ import annotations + +import weakref +from collections.abc import Callable +from typing import Any, Literal, overload + + +_curried = weakref.WeakKeyDictionary[Any, Any]() + + +def _curry_n(f: Callable[..., Any], arity: int, args: tuple[Any, ...] = ()) -> Callable[..., Any]: + """Dynamic curry implementation that builds nested lambdas.""" + return lambda a: f(*args, a) if arity == 1 else _curry_n(f, arity - 1, (*args, a)) + + +def _uncurry_n(f: Callable[..., Any], arity: int) -> Callable[..., Any]: + """Dynamic uncurry implementation.""" + + def uncurried(*args: Any) -> Any: + result = f + for arg in args: + result = result(arg) + return result + + _curried[uncurried] = f # Memoize the relationship + return uncurried + + +# Overloads for curry with precise typing +@overload +def curry[T1, T2, TResult]( + arity: Literal[2], f: Callable[[T1, T2], TResult] +) -> Callable[[T1], Callable[[T2], TResult]]: ... + + +@overload +def curry[T1, T2, T3, TResult]( + arity: Literal[3], f: Callable[[T1, T2, T3], TResult] +) -> Callable[[T1], Callable[[T2], Callable[[T3], TResult]]]: ... + + +@overload +def curry[T1, T2, T3, T4, TResult]( + arity: Literal[4], f: Callable[[T1, T2, T3, T4], TResult] +) -> Callable[[T1], Callable[[T2], Callable[[T3], Callable[[T4], TResult]]]]: ... + + +@overload +def curry[T1, T2, T3, T4, T5, TResult]( + arity: Literal[5], f: Callable[[T1, T2, T3, T4, T5], TResult] +) -> Callable[[T1], Callable[[T2], Callable[[T3], Callable[[T4], Callable[[T5], TResult]]]]]: ... + + +# Fallback for higher arities +@overload +def curry(arity: int, f: Callable[..., Any]) -> Callable[..., Any]: ... + + +def curry(arity: int, f: Callable[..., Any]) -> Callable[..., Any]: + """Curry a function with the given arity. + + If f was previously created by uncurry(), returns the original curried function. + """ + cached = _curried.get(f) + if cached is not None: + return cached + return _curry_n(f, arity) + + +# Overloads for uncurry with precise typing +@overload +def uncurry[T1, T2, TResult]( + arity: Literal[2], f: Callable[[T1], Callable[[T2], TResult]] +) -> Callable[[T1, T2], TResult]: ... + + +@overload +def uncurry[T1, T2, T3, TResult]( + arity: Literal[3], f: Callable[[T1], Callable[[T2], Callable[[T3], TResult]]] +) -> Callable[[T1, T2, T3], TResult]: ... + + +@overload +def uncurry[T1, T2, T3, T4, TResult]( + arity: Literal[4], f: Callable[[T1], Callable[[T2], Callable[[T3], Callable[[T4], TResult]]]] +) -> Callable[[T1, T2, T3, T4], TResult]: ... + + +@overload +def uncurry[T1, T2, T3, T4, T5, TResult]( + arity: Literal[5], f: Callable[[T1], Callable[[T2], Callable[[T3], Callable[[T4], Callable[[T5], TResult]]]]] +) -> Callable[[T1, T2, T3, T4, T5], TResult]: ... + + +# Fallback for higher arities +@overload +def uncurry(arity: int, f: Callable[..., Any]) -> Callable[..., Any]: ... + + +def uncurry(arity: int, f: Callable[..., Any]) -> Callable[..., Any]: + """Uncurry a function with the given arity.""" + return _uncurry_n(f, arity) + + +__all__ = [ + "curry", + "uncurry", +] diff --git a/src/fable-library-py/fable_library/date.py b/src/fable-library-py/fable_library/date.py index 5a2dc52bf..52853d820 100644 --- a/src/fable-library-py/fable_library/date.py +++ b/src/fable-library-py/fable_library/date.py @@ -5,10 +5,10 @@ from math import fmod from typing import Any, SupportsInt, overload +from .core import FSharpRef, float64, int32, int64 from .singleton_local_time_zone import local_time_zone from .time_span import TimeSpan, total_microseconds from .time_span import create as create_time_span -from .types import FSharpRef, float64, int32, int64 from .util import DateKind diff --git a/src/fable-library-py/fable_library/date_offset.py b/src/fable-library-py/fable_library/date_offset.py index 13f6d0f66..22dbd0285 100644 --- a/src/fable-library-py/fable_library/date_offset.py +++ b/src/fable-library-py/fable_library/date_offset.py @@ -4,8 +4,8 @@ from typing import Any, SupportsIndex from . import time_span +from .core import FSharpRef from .time_span import TimeSpan -from .types import FSharpRef class DateTimeOffset(datetime): diff --git a/src/fable-library-py/fable_library/decimal_.py b/src/fable-library-py/fable_library/decimal_.py index 53a70fb1d..5d77e34f0 100644 --- a/src/fable-library-py/fable_library/decimal_.py +++ b/src/fable-library-py/fable_library/decimal_.py @@ -1,6 +1,7 @@ from decimal import MAX_EMAX, MIN_EMIN, Decimal, getcontext -from .types import FSharpRef, IntegerTypes, byte, float32, float64, int16, int32, int64, sbyte, uint16, uint32, uint64 +from .core import FSharpRef, byte, float32, float64, int16, int32, int64, sbyte, uint16, uint32, uint64 +from .types import IntegerTypes getcontext().prec = 29 diff --git a/src/fable-library-py/fable_library/diagnostics.py b/src/fable-library-py/fable_library/diagnostics.py index f36d01012..346e37a45 100644 --- a/src/fable-library-py/fable_library/diagnostics.py +++ b/src/fable-library-py/fable_library/diagnostics.py @@ -2,7 +2,7 @@ import time -from .types import int32, int64 +from .core import int32, int64 class TimerError(Exception): diff --git a/src/fable-library-py/fable_library/double.py b/src/fable-library-py/fable_library/double.py index 9515102ef..0324fa982 100644 --- a/src/fable-library-py/fable_library/double.py +++ b/src/fable-library-py/fable_library/double.py @@ -1,7 +1,6 @@ from typing import Any -from .core import float32, float64, floats -from .types import FSharpRef +from .core import FSharpRef, float32, float64, floats def sign(x: float64) -> float64: diff --git a/src/fable-library-py/fable_library/exceptions.py b/src/fable-library-py/fable_library/exceptions.py new file mode 100644 index 000000000..963e3d6e1 --- /dev/null +++ b/src/fable-library-py/fable_library/exceptions.py @@ -0,0 +1,126 @@ +"""F# Exception types for Fable Python runtime.""" + +from __future__ import annotations + +from collections.abc import Iterable +from typing import Any, cast + +from .bases import ComparableBase, EquatableBase, HashableBase, StringableBase +from .protocols import IComparable + + +def _exception_to_string(self: FSharpException) -> str: + """Convert FSharpException to string representation.""" + if hasattr(self, "__slots__"): + return "{ " + "\n ".join(map(lambda slot: slot + " = " + str(getattr(self, slot)), self.__slots__)) + " }" + else: + return "{ " + "\n ".join(map(lambda kv: kv[0] + " = " + str(kv[1]), self.__dict__.items())) + " }" + + +class ObjectDisposedException(Exception): + """Exception thrown when accessing a disposed object.""" + + def __init__(self) -> None: + super().__init__("Cannot access a disposed object") + + +def seq_to_string(self: Iterable[Any]) -> str: + str = "[" + + for count, x in enumerate(self): + if count == 0: + str += to_string(x) + + elif count == 100: + str += "; ..." + break + + else: + str += "; " + to_string(x) + + return str + "]" + + +def to_string(x: object | None, call_stack: int = 0) -> str: + match x: + case float() if int(x) == x: + return str(int(x)) + case bool(): + return str(x).lower() + case Iterable() if not hasattr(cast(Iterable[Any], x), "__str__"): + return seq_to_string(cast(Iterable[Any], x)) + case _: + return str(x) + + +class FSharpException(StringableBase, EquatableBase, ComparableBase, HashableBase, Exception, IComparable): + """Base class for F# exceptions. + + Inherits from ABC base classes that provide Python dunder methods: + - StringableBase: __str__, __repr__ from ToString + - EquatableBase: __eq__, __ne__ from Equals + - ComparableBase: __lt__, __le__, __gt__, __ge__ from CompareTo + - HashableBase: __hash__ from GetHashCode + """ + + def __init__(self) -> None: + self.Data0: Any = None + + # ------------------------------------------------------------------------- + # String representation (used by StringableBase) + # ------------------------------------------------------------------------- + + def ToString(self) -> str: + return _exception_to_string(self) + + # ------------------------------------------------------------------------- + # IEquatable - Equality (used by EquatableBase) + # ------------------------------------------------------------------------- + + def Equals(self, other: Any) -> bool: + if self is other: + return True + + if other is None: + return False + + return self.Data0 == other.Data0 + + # ------------------------------------------------------------------------- + # Hashable (HashableBase provides __hash__ from GetHashCode) + # ------------------------------------------------------------------------- + + def GetHashCode(self) -> int: + return hash(self.Data0) + + # ------------------------------------------------------------------------- + # IComparable - Comparison (used by ComparableBase) + # ------------------------------------------------------------------------- + + def CompareTo(self, other: Any) -> int: + if not isinstance(other, FSharpException): + return 1 # Non-comparable types sort after + # Simple comparison for Data0 (avoid circular import with util.compare) + a, b = self.Data0, other.Data0 + if a is b: + return 0 + if a is None: + return -1 if b else 0 + if b is None: + return 1 if a else 0 + if hasattr(a, "CompareTo") and callable(a.CompareTo): + return cast(int, a.CompareTo(b)) + return 0 if a == b else (-1 if a < b else 1) + + +def is_exception(x: Any) -> bool: + return isinstance(x, Exception) + + +__all__ = [ + "FSharpException", + "ObjectDisposedException", + "is_exception", + "seq_to_string", + "to_string", +] diff --git a/src/fable-library-py/fable_library/guid.py b/src/fable-library-py/fable_library/guid.py index 62d3fdb38..b1a291fe8 100644 --- a/src/fable-library-py/fable_library/guid.py +++ b/src/fable-library-py/fable_library/guid.py @@ -1,9 +1,7 @@ import uuid -from fable_library.array_ import Array -from fable_library.types import byte - -from .types import FSharpRef +from .array_ import Array +from .core import FSharpRef, byte def parse(string: str) -> uuid.UUID: diff --git a/src/fable-library-py/fable_library/long.py b/src/fable-library-py/fable_library/long.py index d7c519998..50d7ed84c 100644 --- a/src/fable-library-py/fable_library/long.py +++ b/src/fable-library-py/fable_library/long.py @@ -1,9 +1,9 @@ from typing import Any, Literal, SupportsFloat, SupportsInt, overload +from .core import float64, int32, int64, uint64 from .core._core import get_range_64 as get_range from .core._core import parse_int64 as parse from .core._core import try_parse_int64 as try_parse -from .types import float64, int32, int64, uint64 def compare(x: int64, y: int64) -> int64: diff --git a/src/fable-library-py/fable_library/map_util.py b/src/fable-library-py/fable_library/map_util.py index 07d785422..e309d70ec 100644 --- a/src/fable-library-py/fable_library/map_util.py +++ b/src/fable-library-py/fable_library/map_util.py @@ -12,8 +12,9 @@ ) from .array_ import Array +from .core import FSharpRef from .protocols import IEnumerable_1 -from .types import FSharpRef, Union +from .union import Union from .util import to_iterable diff --git a/src/fable-library-py/fable_library/observable.py b/src/fable-library-py/fable_library/observable.py index 0488c534e..9c5cef70a 100644 --- a/src/fable-library-py/fable_library/observable.py +++ b/src/fable-library-py/fable_library/observable.py @@ -9,7 +9,7 @@ ) from .option import Option, value from .protocols import IDisposable -from .util import UNIT, Disposable +from .util import Disposable @runtime_checkable @@ -26,7 +26,7 @@ def OnError(self, __error: Exception) -> None: ... def OnCompleted(self) -> None: ... -def _noop(__arg=UNIT) -> None: +def _noop(__arg: object = None) -> None: pass diff --git a/src/fable-library-py/fable_library/protocols.py b/src/fable-library-py/fable_library/protocols.py index 9525e7787..61cf8bd1e 100644 --- a/src/fable-library-py/fable_library/protocols.py +++ b/src/fable-library-py/fable_library/protocols.py @@ -16,6 +16,8 @@ from collections.abc import Iterable from typing import TYPE_CHECKING, Any, Protocol, runtime_checkable +from .types import UNIT, Unit + if TYPE_CHECKING: from .core import int32 @@ -178,14 +180,14 @@ def CompareTo(self, other: Any, comparer: IComparer) -> int32: class IGenericAdder[T](Protocol): """Protocol for types that support addition with a zero value.""" - def GetZero(self, __unit: Any = ...) -> T: ... + def GetZero(self, __unit: Unit = UNIT) -> T: ... def Add(self, x: T, y: T, /) -> T: ... class IGenericAverager[T](Protocol): """Protocol for types that support averaging (add, divide by count).""" - def GetZero(self, __unit: Any = ...) -> T: ... + def GetZero(self, __unit: Unit = UNIT) -> T: ... def Add(self, x: T, y: T, /) -> T: ... def DivideByInt(self, x: T, i: int32, /) -> T: ... @@ -247,9 +249,6 @@ def System_Collections_IEnumerator_Reset(self) -> None: ... # Enumerable Protocols # ============================================================================= -# Note: UNIT is not imported here to avoid circular dependency. -# The __unit parameter uses Any default which works the same way. - class IEnumerable(Protocol): """Protocol for enumerable collections (pure protocol for type hints). @@ -260,7 +259,7 @@ class IEnumerable(Protocol): __slots__ = () - def GetEnumerator(self, __unit: Any = None) -> IEnumerator[Any]: ... + def GetEnumerator(self, __unit: Unit = UNIT) -> IEnumerator[Any]: ... class IEnumerable_1[T](Protocol): @@ -272,7 +271,7 @@ class IEnumerable_1[T](Protocol): __slots__ = () - def GetEnumerator(self, __unit: Any = None) -> IEnumerator[T]: ... + def GetEnumerator(self, __unit: Unit = UNIT) -> IEnumerator[T]: ... # ============================================================================= @@ -436,7 +435,7 @@ class IReadOnlyDictionary[K, V](Protocol): For the full .NET IReadOnlyDictionary, use IReadOnlyDictionary_2. """ - def keys(self, __unit: Any = None) -> Iterable[K]: + def keys(self, __unit: Unit = None) -> Iterable[K]: """Return an iterable of keys.""" ... diff --git a/src/fable-library-py/fable_library/record.py b/src/fable-library-py/fable_library/record.py new file mode 100644 index 000000000..d43d0f0b5 --- /dev/null +++ b/src/fable-library-py/fable_library/record.py @@ -0,0 +1,137 @@ +"""F# Record type for Fable Python runtime.""" + +from __future__ import annotations + +from .bases import ComparableBase, EquatableBase, HashableBase, StringableBase +from .core import int32 +from .protocols import IComparable +from .util import compare, equals + + +def record_compare_to(self: Record, other: Record) -> int: + if self is other: + return 0 + + # Check if the record has a custom __eq__ method (not inherited from Record) + # If so, use it for equality-based comparison instead of field-by-field + self_eq_method = getattr(type(self), "__eq__", None) + record_eq_method = getattr(Record, "__eq__", None) + + if self_eq_method and self_eq_method != record_eq_method: + # Record has custom equality, use it for comparison + if self == other: + return 0 + # For custom equality, we can't determine ordering, so use identity comparison + return -1 if id(self) < id(other) else 1 + + # Default field-by-field comparison for records without custom equality + if hasattr(self, "__dict__") and self.__dict__: + for name in self.__dict__.keys(): + result = compare(self.__dict__[name], other.__dict__[name]) + if result != 0: + return result + + elif hasattr(self, "__slots__") and self.__slots__: + for name in self.__slots__: + result = compare(getattr(self, name), getattr(other, name)) + if result != 0: + return result + + return 0 + + +def record_equals[T](self: T, other: T) -> bool: + if self is other: + return True + + if self.__class__ != other.__class__: + return False + + if isinstance(self, Record) and isinstance(other, Record): + # Check if the record has a custom __eq__ method (not inherited from Record) + # If so, use it for equality + self_eq_method = getattr(type(self), "__eq__", None) + record_eq_method = getattr(Record, "__eq__", None) + + if self_eq_method and self_eq_method != record_eq_method: + # Record has custom equality, use it + return self == other + + # Default field-by-field comparison using equals() for proper nested equality + if hasattr(self, "__dict__") and self.__dict__: + for name in self.__dict__.keys(): + if not equals(self.__dict__[name], other.__dict__[name]): + return False + elif hasattr(self, "__slots__") and self.__slots__: + for name in self.__slots__: + if not equals(getattr(self, name), getattr(other, name)): + return False + return True + + return self == other + + +def record_to_string(self: Record) -> str: + if hasattr(self, "__slots__"): + return "{ " + "\n ".join(map(lambda slot: slot + " = " + str(getattr(self, slot)), self.__slots__)) + " }" + else: + return "{ " + "\n ".join(map(lambda kv: kv[0] + " = " + str(kv[1]), self.__dict__.items())) + " }" + + +def record_get_hashcode(self: Record) -> int: + slots = type(self).__slots__ + return hash(tuple(getattr(self, fixed_field) for fixed_field in slots)) + + +class Record(StringableBase, EquatableBase, ComparableBase, HashableBase, IComparable): + """Base class for F# records. + + Inherits from ABC base classes that provide Python dunder methods: + - StringableBase: __str__, __repr__ from ToString + - EquatableBase: __eq__, __ne__ from Equals + - ComparableBase: __lt__, __le__, __gt__, __ge__ from CompareTo + - HashableBase: __hash__ from GetHashCode + """ + + __slots__: list[str] = [] + + # ------------------------------------------------------------------------- + # String representation (used by StringableBase) + # ------------------------------------------------------------------------- + + def ToString(self) -> str: + return record_to_string(self) + + # ------------------------------------------------------------------------- + # IEquatable - Equality (used by EquatableBase) + # ------------------------------------------------------------------------- + + def Equals(self, other: object) -> bool: + if not isinstance(other, Record): + return False + return record_equals(self, other) + + # ------------------------------------------------------------------------- + # Hashable (HashableBase provides __hash__ from GetHashCode) + # ------------------------------------------------------------------------- + + def GetHashCode(self) -> int32: + return int32(record_get_hashcode(self)) + + # ------------------------------------------------------------------------- + # IComparable - Comparison (used by ComparableBase) + # ------------------------------------------------------------------------- + + def CompareTo(self, other: object) -> int: + if not isinstance(other, Record): + raise TypeError(f"Cannot compare Record with {type(other).__name__}") + return record_compare_to(self, other) + + +__all__ = [ + "Record", + "record_compare_to", + "record_equals", + "record_get_hashcode", + "record_to_string", +] diff --git a/src/fable-library-py/fable_library/reflection.py b/src/fable-library-py/fable_library/reflection.py index 75195458b..0f0165ca2 100644 --- a/src/fable-library-py/fable_library/reflection.py +++ b/src/fable-library-py/fable_library/reflection.py @@ -5,12 +5,16 @@ from dataclasses import dataclass from typing import Any, cast -from .types import Array, FSharpRef, IntegerTypes, Record, Union, int32 -from .types import Union as FsUnion +from .array_ import Array +from .core import FSharpRef, int32 +from .record import Record +from .types import IntegerTypes +from .union import Union +from .union import Union as FsUnion from .util import combine_hash_codes, equal_arrays_with -Constructor = Callable[..., Any] +Constructor = type[Any] EnumCase = tuple[str, IntegerTypes] FieldInfo = tuple[str, "TypeInfo"] @@ -251,7 +255,7 @@ def is_instance_of_type(t: TypeInfo, o: Any) -> bool: if callable(o): return is_function(t) - return t.construct is not None and isinstance(o, t.construct) # type: ignore + return t.construct is not None and isinstance(o, t.construct) def is_record(t: Any) -> bool: diff --git a/src/fable-library-py/fable_library/reg_exp.py b/src/fable-library-py/fable_library/reg_exp.py index fc0557298..5dc5f1f5c 100644 --- a/src/fable-library-py/fable_library/reg_exp.py +++ b/src/fable-library-py/fable_library/reg_exp.py @@ -7,7 +7,7 @@ from .array_ import Array from .protocols import IEnumerator from .types import IntegerTypes -from .util import UNIT, Enumerator +from .util import UNIT, Enumerator, Unit MatchEvaluator = Callable[[Match[str]], str] @@ -33,7 +33,7 @@ def __getitem__(self, key: int | str) -> str | None: def __iter__(self) -> Iterator[str]: return iter(self.groups) - def GetEnumerator(self, __unit=UNIT) -> IEnumerator[str]: + def GetEnumerator(self, __unit: Unit = UNIT) -> IEnumerator[str]: return Enumerator(iter(self.groups)) @@ -52,7 +52,7 @@ def __getitem__(self, index: int) -> Match[str]: def __iter__(self) -> Iterator[Match[str]]: return iter(self._matches) - def GetEnumerator(self, __unit=UNIT) -> IEnumerator[Match[str]]: + def GetEnumerator(self, __unit: Unit = UNIT) -> IEnumerator[Match[str]]: return Enumerator(iter(self._matches)) diff --git a/src/fable-library-py/fable_library/resize_array.py b/src/fable-library-py/fable_library/resize_array.py index cdd8cd532..c906c596f 100644 --- a/src/fable-library-py/fable_library/resize_array.py +++ b/src/fable-library-py/fable_library/resize_array.py @@ -3,9 +3,9 @@ from collections.abc import Callable, Iterable from typing import Any, cast +from .core import int32 from .option import Option, some from .protocols import IEnumerable_1 -from .types import int32 from .util import to_iterable diff --git a/src/fable-library-py/fable_library/string_.py b/src/fable-library-py/fable_library/string_.py index 2c9d8ef63..eff1689c4 100644 --- a/src/fable-library-py/fable_library/string_.py +++ b/src/fable-library-py/fable_library/string_.py @@ -4,8 +4,8 @@ from base64 import b64decode, b64encode from typing import Any -from .core import byte, strings -from .types import Array, int32 +from .array_ import Array +from .core import byte, int32, strings # Re-export classes from core.strings diff --git a/src/fable-library-py/fable_library/task_builder.py b/src/fable-library-py/fable_library/task_builder.py index a298cac87..41921d3d5 100644 --- a/src/fable-library-py/fable_library/task_builder.py +++ b/src/fable-library-py/fable_library/task_builder.py @@ -8,11 +8,11 @@ ) from .task import from_result, zero -from .util import UNIT, IDisposable +from .util import UNIT, IDisposable, Unit class Delayed[T](Protocol): - def __call__(self, __unit=UNIT) -> Awaitable[T]: ... + def __call__(self, __unit: Unit = UNIT) -> Awaitable[T]: ... class TaskBuilder: diff --git a/src/fable-library-py/fable_library/time_span.py b/src/fable-library-py/fable_library/time_span.py index 41218d305..2a1008441 100644 --- a/src/fable-library-py/fable_library/time_span.py +++ b/src/fable-library-py/fable_library/time_span.py @@ -4,7 +4,8 @@ from math import ceil, floor, fmod from typing import Any, SupportsFloat, SupportsInt -from .types import FloatTypes, FSharpRef, IntegerTypes, float64 +from .core import FSharpRef, float64 +from .types import FloatTypes, IntegerTypes from .util import pad_left_and_right_with_zeros, pad_with_zeros diff --git a/src/fable-library-py/fable_library/types.py b/src/fable-library-py/fable_library/types.py index 05e92623f..ba3afb191 100644 --- a/src/fable-library-py/fable_library/types.py +++ b/src/fable-library-py/fable_library/types.py @@ -1,325 +1,59 @@ -from __future__ import annotations - -from abc import abstractmethod -from collections.abc import Iterable -from typing import ( - Any, - cast, -) - -from .array_ import Array -from .bases import ComparableBase, EquatableBase, HashableBase, StringableBase -from .core import FSharpRef, byte, float32, float64, int8, int16, int32, int64, sbyte, uint8, uint16, uint32, uint64 -from .protocols import IComparable -from .util import compare, equals - - -class Union(StringableBase, EquatableBase, ComparableBase, HashableBase, IComparable): - """Base class for F# discriminated unions. - - Inherits from ABC base classes that provide Python dunder methods: - - StringableBase: __str__, __repr__ from ToString - - EquatableBase: __eq__, __ne__ from Equals - - ComparableBase: __lt__, __le__, __gt__, __ge__ from CompareTo - - HashableBase: __hash__ from GetHashCode - """ - - __slots__: list[str] = ["fields", "tag"] - - tag: int32 - fields: Array[Any] - - def __init__(self) -> None: - self.fields = Array[Any]() - - @staticmethod - @abstractmethod - def cases() -> list[str]: ... - - @property - def name(self) -> str: - return self.cases()[self.tag] - - # ------------------------------------------------------------------------- - # String representation (used by StringableBase) - # ------------------------------------------------------------------------- - - def ToString(self) -> str: - if not len(self.fields): - return self.name - - def to_string(value: Any) -> str: - if isinstance(value, str): - return f'"{value}"' - return str(value) - - fields = "" - with_parens = True - if len(self.fields) == 1: - field = to_string(self.fields[0]) - with_parens = field.find(" ") >= 0 - fields = field - else: - fields = ", ".join(map(to_string, self.fields)) - - return self.name + (" (" if with_parens else " ") + fields + (")" if with_parens else "") - - # ------------------------------------------------------------------------- - # IEquatable - Equality (used by EquatableBase) - # ------------------------------------------------------------------------- - - def Equals(self, other: Any) -> bool: - if self is other: - return True - - if not isinstance(other, Union): - return False - - # Different objects are not equal even with same structure - if self.__class__ != other.__class__: - return False - - if self.tag == other.tag: - return self.fields == other.fields - - return False - - # ------------------------------------------------------------------------- - # Hashable (HashableBase provides __hash__ from GetHashCode) - # ------------------------------------------------------------------------- - - def GetHashCode(self) -> int32: - return int32(hash((self.tag, *self.fields))) - - # ------------------------------------------------------------------------- - # IComparable - Comparison (used by ComparableBase) - # ------------------------------------------------------------------------- - - def CompareTo(self, other: Any) -> int: - if self.tag == other.tag: - return compare(self.fields, other.fields) - return -1 if self.tag < other.tag else 1 - - -def record_compare_to(self: Record, other: Record) -> int: - if self is other: - return 0 - - # Check if the record has a custom __eq__ method (not inherited from Record) - # If so, use it for equality-based comparison instead of field-by-field - self_eq_method = getattr(type(self), "__eq__", None) - record_eq_method = getattr(Record, "__eq__", None) - - if self_eq_method and self_eq_method != record_eq_method: - # Record has custom equality, use it for comparison - if self == other: - return 0 - # For custom equality, we can't determine ordering, so use identity comparison - return -1 if id(self) < id(other) else 1 - - # Default field-by-field comparison for records without custom equality - if hasattr(self, "__dict__") and self.__dict__: - for name in self.__dict__.keys(): - result = compare(self.__dict__[name], other.__dict__[name]) - if result != 0: - return result - - elif hasattr(self, "__slots__") and self.__slots__: - for name in self.__slots__: - result = compare(getattr(self, name), getattr(other, name)) - if result != 0: - return result - - return 0 - - -def record_equals[T](self: T, other: T) -> bool: - if self is other: - return True - - if self.__class__ != other.__class__: - return False - - if isinstance(self, Record) and isinstance(other, Record): - # Check if the record has a custom __eq__ method (not inherited from Record) - # If so, use it for equality - self_eq_method = getattr(type(self), "__eq__", None) - record_eq_method = getattr(Record, "__eq__", None) +"""Core type definitions for Fable Python runtime. - if self_eq_method and self_eq_method != record_eq_method: - # Record has custom equality, use it - return self == other +This module contains fundamental type definitions used throughout the +Fable runtime library. +""" - # Default field-by-field comparison using equals() for proper nested equality - if hasattr(self, "__dict__") and self.__dict__: - for name in self.__dict__.keys(): - if not equals(self.__dict__[name], other.__dict__[name]): - return False - elif hasattr(self, "__slots__") and self.__slots__: - for name in self.__slots__: - if not equals(getattr(self, name), getattr(other, name)): - return False - return True - - return self == other - - -def record_to_string(self: Record) -> str: - if hasattr(self, "__slots__"): - return "{ " + "\n ".join(map(lambda slot: slot + " = " + str(getattr(self, slot)), self.__slots__)) + " }" - else: - return "{ " + "\n ".join(map(lambda kv: kv[0] + " = " + str(kv[1]), self.__dict__.items())) + " }" - - -def record_get_hashcode(self: Record) -> int: - slots = type(self).__slots__ - return hash(tuple(getattr(self, fixed_field) for fixed_field in slots)) - - -class Record(StringableBase, EquatableBase, ComparableBase, HashableBase, IComparable): - """Base class for F# records. - - Inherits from ABC base classes that provide Python dunder methods: - - StringableBase: __str__, __repr__ from ToString - - EquatableBase: __eq__, __ne__ from Equals - - ComparableBase: __lt__, __le__, __gt__, __ge__ from CompareTo - - HashableBase: __hash__ from GetHashCode - """ - - __slots__: list[str] = [] - - # ------------------------------------------------------------------------- - # String representation (used by StringableBase) - # ------------------------------------------------------------------------- - - def ToString(self) -> str: - return record_to_string(self) - - # ------------------------------------------------------------------------- - # IEquatable - Equality (used by EquatableBase) - # ------------------------------------------------------------------------- - - def Equals(self, other: object) -> bool: - if not isinstance(other, Record): - return False - return record_equals(self, other) - - # ------------------------------------------------------------------------- - # Hashable (HashableBase provides __hash__ from GetHashCode) - # ------------------------------------------------------------------------- - - def GetHashCode(self) -> int32: - return int32(record_get_hashcode(self)) - - # ------------------------------------------------------------------------- - # IComparable - Comparison (used by ComparableBase) - # ------------------------------------------------------------------------- - - def CompareTo(self, other: object) -> int: - if not isinstance(other, Record): - raise TypeError(f"Cannot compare Record with {type(other).__name__}") - return record_compare_to(self, other) - - -class Attribute: ... - - -def seq_to_string(self: Iterable[Any]) -> str: - str = "[" - - for count, x in enumerate(self): - if count == 0: - str += to_string(x) - - elif count == 100: - str += "; ..." - break - - else: - str += "; " + to_string(x) - - return str + "]" - - -def to_string(x: object | None, call_stack: int = 0) -> str: - match x: - case float() if int(x) == x: - return str(int(x)) - case bool(): - return str(x).lower() - case Iterable() if not hasattr(cast(Iterable[Any], x), "__str__"): - return seq_to_string(cast(Iterable[Any], x)) - case _: - return str(x) - - -class FSharpException(StringableBase, EquatableBase, ComparableBase, HashableBase, Exception, IComparable): - """Base class for F# exceptions. - - Inherits from ABC base classes that provide Python dunder methods: - - StringableBase: __str__, __repr__ from ToString - - EquatableBase: __eq__, __ne__ from Equals - - ComparableBase: __lt__, __le__, __gt__, __ge__ from CompareTo - - HashableBase: __hash__ from GetHashCode - """ - - def __init__(self) -> None: - self.Data0: Any = None - - # ------------------------------------------------------------------------- - # String representation (used by StringableBase) - # ------------------------------------------------------------------------- - - def ToString(self) -> str: - return record_to_string(self) # type: ignore[arg-type] - - # ------------------------------------------------------------------------- - # IEquatable - Equality (used by EquatableBase) - # ------------------------------------------------------------------------- +from __future__ import annotations - def Equals(self, other: Any) -> bool: - if self is other: - return True +from typing import Any + +from .core import ( + FSharpRef, + byte, + float32, + float64, + int8, + int16, + int32, + int64, + sbyte, + uint8, + uint16, + uint32, + uint64, +) - if other is None: - return False - return self.Data0 == other.Data0 +# Unit type for F# unit-typed parameters. Using None mirrors F#'s +# unit type which is essentially a 0-tuple with a single value (). +# This provides a concrete type for strict type checking instead of Any. +type Unit = None - # ------------------------------------------------------------------------- - # Hashable (HashableBase provides __hash__ from GetHashCode) - # ------------------------------------------------------------------------- +# UNIT is typed as Any so it can be used as a default value for generic type +# parameters like `def foo[T](x: T = UNIT)`. None would not be +# assignable to T, but Any is compatible with all types. +UNIT: Any = None - def GetHashCode(self) -> int: - return hash(self.Data0) - # ------------------------------------------------------------------------- - # IComparable - Comparison (used by ComparableBase) - # ------------------------------------------------------------------------- +class Attribute: + """Base class for F# attributes.""" - def CompareTo(self, other: Any) -> int: - if not isinstance(other, FSharpException): - return 1 # Non-comparable types sort after - return compare(self.Data0, other.Data0) + ... -# We don't use type aliases here because since we need to do isinstance checks +# We don't use type aliases here because we need to do isinstance checks IntegerTypes = int | byte | sbyte | int16 | uint16 | int32 | uint32 | int64 | uint64 FloatTypes = float | float32 | float64 -def is_exception(x: Any): - return isinstance(x, Exception) - - __all__ = [ - "Array", + "UNIT", "Attribute", - "FSharpException", "FSharpRef", "FloatTypes", "IntegerTypes", - "Union", + "Unit", "byte", "float32", "float64", @@ -327,10 +61,7 @@ def is_exception(x: Any): "int16", "int32", "int64", - "is_exception", "sbyte", - "seq_to_string", - "to_string", "uint8", "uint16", "uint32", diff --git a/src/fable-library-py/fable_library/union.py b/src/fable-library-py/fable_library/union.py new file mode 100644 index 000000000..bd76916e8 --- /dev/null +++ b/src/fable-library-py/fable_library/union.py @@ -0,0 +1,104 @@ +"""F# Union (Discriminated Union) type for Fable Python runtime.""" + +from __future__ import annotations + +from abc import abstractmethod +from typing import Any + +from .array_ import Array +from .bases import ComparableBase, EquatableBase, HashableBase, StringableBase +from .core import int32 +from .protocols import IComparable +from .util import compare + + +class Union(StringableBase, EquatableBase, ComparableBase, HashableBase, IComparable): + """Base class for F# discriminated unions. + + Inherits from ABC base classes that provide Python dunder methods: + - StringableBase: __str__, __repr__ from ToString + - EquatableBase: __eq__, __ne__ from Equals + - ComparableBase: __lt__, __le__, __gt__, __ge__ from CompareTo + - HashableBase: __hash__ from GetHashCode + """ + + __slots__: list[str] = ["fields", "tag"] + + tag: int32 + fields: Array[Any] + + def __init__(self) -> None: + self.fields = Array[Any]() + + @staticmethod + @abstractmethod + def cases() -> list[str]: ... + + @property + def name(self) -> str: + return self.cases()[self.tag] + + # ------------------------------------------------------------------------- + # String representation (used by StringableBase) + # ------------------------------------------------------------------------- + + def ToString(self) -> str: + if not len(self.fields): + return self.name + + def to_string(value: Any) -> str: + if isinstance(value, str): + return f'"{value}"' + return str(value) + + fields = "" + with_parens = True + if len(self.fields) == 1: + field = to_string(self.fields[0]) + with_parens = field.find(" ") >= 0 + fields = field + else: + fields = ", ".join(map(to_string, self.fields)) + + return self.name + (" (" if with_parens else " ") + fields + (")" if with_parens else "") + + # ------------------------------------------------------------------------- + # IEquatable - Equality (used by EquatableBase) + # ------------------------------------------------------------------------- + + def Equals(self, other: Any) -> bool: + if self is other: + return True + + if not isinstance(other, Union): + return False + + # Different objects are not equal even with same structure + if self.__class__ != other.__class__: + return False + + if self.tag == other.tag: + return self.fields == other.fields + + return False + + # ------------------------------------------------------------------------- + # Hashable (HashableBase provides __hash__ from GetHashCode) + # ------------------------------------------------------------------------- + + def GetHashCode(self) -> int32: + return int32(hash((self.tag, *self.fields))) + + # ------------------------------------------------------------------------- + # IComparable - Comparison (used by ComparableBase) + # ------------------------------------------------------------------------- + + def CompareTo(self, other: Any) -> int: + if self.tag == other.tag: + return compare(self.fields, other.fields) + return -1 if self.tag < other.tag else 1 + + +__all__ = [ + "Union", +] diff --git a/src/fable-library-py/fable_library/uri.py b/src/fable-library-py/fable_library/uri.py index 7981e1a8b..5400b35d4 100644 --- a/src/fable-library-py/fable_library/uri.py +++ b/src/fable-library-py/fable_library/uri.py @@ -3,7 +3,8 @@ from enum import IntEnum from urllib.parse import ParseResult, unquote, urljoin, urlparse -from .types import FSharpRef, IntegerTypes, int32 +from .core import FSharpRef, int32 +from .types import IntegerTypes class UriKind(IntEnum): diff --git a/src/fable-library-py/fable_library/util.py b/src/fable-library-py/fable_library/util.py index cc51802fe..4d8fbb496 100644 --- a/src/fable-library-py/fable_library/util.py +++ b/src/fable-library-py/fable_library/util.py @@ -5,7 +5,6 @@ import platform import random import re -import weakref from abc import ABC, ABCMeta, abstractmethod from collections.abc import ( Callable, @@ -45,6 +44,7 @@ from .core import float32, float64, int32 # Re-export protocols for backward compatibility +from .exceptions import ObjectDisposedException from .protocols import ( HashCode, IDisposable, @@ -52,12 +52,7 @@ IEnumerator, SupportsLessThan, ) - - -# Unit type for F# unit-typed parameters. Using Any allows it to be a valid -# default value for any generic type T, preserving generic constraints in -# signatures like `def foo[T](x: T = UNIT) -> T` instead of `x: T | None = None` -UNIT: Any = None +from .types import UNIT, Unit class nullable: @@ -95,13 +90,6 @@ def __new__(cls) -> Any: # These classes provide disposable/context manager implementations. -class ObjectDisposedException(Exception): - """Exception thrown when accessing a disposed object.""" - - def __init__(self) -> None: - super().__init__("Cannot access a disposed object") - - class AnonymousDisposable(DisposableBase): """A disposable that calls a provided action when disposed.""" @@ -511,7 +499,7 @@ class Enumerable[T](EnumerableBase[T]): def __init__(self, xs: Iterable[T]) -> None: self.xs = xs - def GetEnumerator(self, __unit=UNIT) -> IEnumerator[T]: + def GetEnumerator(self, __unit: Unit = UNIT) -> IEnumerator[T]: return Enumerator(iter(self.xs)) def __iter__(self) -> Iterator[T]: @@ -534,1927 +522,6 @@ def get_enumerator(o: IEnumerable_1[Any] | Iterable[Any]) -> Enumerator: return Enumerator(iter(cast(Any, o))) -_curried = weakref.WeakKeyDictionary[Any, Any]() - - -def uncurry2[T1, T2, TResult](f: Callable[[T1], Callable[[T2], TResult]]) -> Callable[[T1, T2], TResult]: - def f2(a1: T1, a2: T2) -> TResult: - return f(a1)(a2) - - _curried[f2] = f - return f2 - - -def curry2[T1, T2, TResult](f: Callable[[T1, T2], TResult]) -> Callable[[T1], Callable[[T2], TResult]]: - f2 = _curried.get(f) - if f2 is None: - return lambda a1: lambda a2: f(a1, a2) - else: - return f2 - - -def uncurry3[T1, T2, T3, TResult]( - f: Callable[[T1], Callable[[T2], Callable[[T3], TResult]]], -) -> Callable[[T1, T2, T3], TResult]: - def f2(a1: T1, a2: T2, a3: T3) -> TResult: - return f(a1)(a2)(a3) - - _curried[f2] = f - return f2 - - -def curry3[T1, T2, T3, TResult]( - f: Callable[[T1, T2, T3], TResult], -) -> Callable[[T1], Callable[[T2], Callable[[T3], TResult]]]: - f2 = _curried.get(f) - if f2 is None: - return lambda a1: lambda a2: lambda a3: f(a1, a2, a3) - else: - return f2 - - -def uncurry4[T1, T2, T3, T4, TResult]( - f: Callable[[T1], Callable[[T2], Callable[[T3], Callable[[T4], TResult]]]], -) -> Callable[[T1, T2, T3, T4], TResult]: - def f2(a1: T1, a2: T2, a3: T3, a4: T4) -> TResult: - return f(a1)(a2)(a3)(a4) - - _curried[f2] = f - return f2 - - -def curry4[T1, T2, T3, T4, TResult]( - f: Callable[[T1, T2, T3, T4], TResult], -) -> Callable[[T1], Callable[[T2], Callable[[T3], Callable[[T4], TResult]]]]: - f2 = _curried.get(f) - if f2 is None: - return lambda a1: lambda a2: lambda a3: lambda a4: f(a1, a2, a3, a4) - else: - return f2 - - -def uncurry5[T1, T2, T3, T4, T5, TResult]( - f: Callable[ - [T1], - Callable[[T2], Callable[[T3], Callable[[T4], Callable[[T5], TResult]]]], - ], -) -> Callable[[T1, T2, T3, T4, T5], TResult]: - def f2(a1: T1, a2: T2, a3: T3, a4: T4, a5: T5) -> TResult: - return f(a1)(a2)(a3)(a4)(a5) - - _curried[f2] = f - return f2 - - -def curry5[T1, T2, T3, T4, T5, TResult]( - f: Callable[[T1, T2, T3, T4, T5], TResult], -) -> Callable[[T1], Callable[[T2], Callable[[T3], Callable[[T4], Callable[[T5], TResult]]]]]: - f2 = _curried.get(f) - if f2 is None: - return lambda a1: lambda a2: lambda a3: lambda a4: lambda a5: f(a1, a2, a3, a4, a5) - else: - return f2 - - -def uncurry6[T1, T2, T3, T4, T5, T6, TResult]( - f: Callable[ - [T1], - Callable[ - [T2], - Callable[[T3], Callable[[T4], Callable[[T5], Callable[[T6], TResult]]]], - ], - ], -) -> Callable[[T1, T2, T3, T4, T5, T6], TResult]: - def f2(a1: T1, a2: T2, a3: T3, a4: T4, a5: T5, a6: T6) -> TResult: - return f(a1)(a2)(a3)(a4)(a5)(a6) - - _curried[f2] = f - return f2 - - -def curry6[T1, T2, T3, T4, T5, T6, TResult]( - f: Callable[[T1, T2, T3, T4, T5, T6], TResult], -) -> Callable[ - [T1], - Callable[ - [T2], - Callable[[T3], Callable[[T4], Callable[[T5], Callable[[T6], TResult]]]], - ], -]: - f2 = _curried.get(f) - if f2 is None: - return lambda a1: lambda a2: lambda a3: lambda a4: lambda a5: lambda a6: f(a1, a2, a3, a4, a5, a6) - else: - return f2 - - -def uncurry7[T1, T2, T3, T4, T5, T6, T7, TResult]( - f: Callable[ - [T1], - Callable[ - [T2], - Callable[ - [T3], - Callable[[T4], Callable[[T5], Callable[[T6], Callable[[T7], TResult]]]], - ], - ], - ], -) -> Callable[[T1, T2, T3, T4, T5, T6, T7], TResult]: - def f2(a1: T1, a2: T2, a3: T3, a4: T4, a5: T5, a6: T6, a7: T7) -> TResult: - return f(a1)(a2)(a3)(a4)(a5)(a6)(a7) - - _curried[f2] = f - return f2 - - -def curry7[T1, T2, T3, T4, T5, T6, T7, TResult]( - f: Callable[[T1, T2, T3, T4, T5, T6, T7], TResult], -) -> Callable[ - [T1], - Callable[ - [T2], - Callable[ - [T3], - Callable[[T4], Callable[[T5], Callable[[T6], Callable[[T7], TResult]]]], - ], - ], -]: - f2 = _curried.get(f) - if f2 is None: - return lambda a1: lambda a2: lambda a3: lambda a4: lambda a5: lambda a6: lambda a7: f( - a1, a2, a3, a4, a5, a6, a7 - ) - else: - return f2 - - -def uncurry8[T1, T2, T3, T4, T5, T6, T7, T8, TResult]( - f: Callable[ - [T1], - Callable[ - [T2], - Callable[ - [T3], - Callable[ - [T4], - Callable[ - [T5], - Callable[[T6], Callable[[T7], Callable[[T8], TResult]]], - ], - ], - ], - ], - ], -) -> Callable[[T1, T2, T3, T4, T5, T6, T7, T8], TResult]: - def f2(a1: T1, a2: T2, a3: T3, a4: T4, a5: T5, a6: T6, a7: T7, a8: T8) -> TResult: - return f(a1)(a2)(a3)(a4)(a5)(a6)(a7)(a8) - - _curried[f2] = f - return f2 - - -def curry8[T1, T2, T3, T4, T5, T6, T7, T8, TResult]( - f: Callable[[T1, T2, T3, T4, T5, T6, T7, T8], TResult], -) -> Callable[ - [T1], - Callable[ - [T2], - Callable[ - [T3], - Callable[ - [T4], - Callable[[T5], Callable[[T6], Callable[[T7], Callable[[T8], TResult]]]], - ], - ], - ], -]: - f2 = _curried.get(f) - if f2 is None: - return lambda a1: lambda a2: lambda a3: lambda a4: lambda a5: lambda a6: lambda a7: lambda a8: f( - a1, a2, a3, a4, a5, a6, a7, a8 - ) - else: - return f2 - - -def uncurry9[T1, T2, T3, T4, T5, T6, T7, T8, T9, TResult]( - f: Callable[ - [T1], - Callable[ - [T2], - Callable[ - [T3], - Callable[ - [T4], - Callable[ - [T5], - Callable[ - [T6], - Callable[[T7], Callable[[T8], Callable[[T9], TResult]]], - ], - ], - ], - ], - ], - ], -) -> Callable[[T1, T2, T3, T4, T5, T6, T7, T8, T9], TResult]: - def f2(a1: T1, a2: T2, a3: T3, a4: T4, a5: T5, a6: T6, a7: T7, a8: T8, a9: T9) -> TResult: - return f(a1)(a2)(a3)(a4)(a5)(a6)(a7)(a8)(a9) - - _curried[f2] = f - return f2 - - -def curry9[T1, T2, T3, T4, T5, T6, T7, T8, T9, TResult]( - f: Callable[[T1, T2, T3, T4, T5, T6, T7, T8, T9], TResult], -) -> Callable[ - [T1], - Callable[ - [T2], - Callable[ - [T3], - Callable[ - [T4], - Callable[ - [T5], - Callable[ - [T6], - Callable[[T7], Callable[[T8], Callable[[T9], TResult]]], - ], - ], - ], - ], - ], -]: - f2 = _curried.get(f) - if f2 is None: - return lambda a1: lambda a2: lambda a3: lambda a4: lambda a5: lambda a6: lambda a7: lambda a8: lambda a9: f( - a1, a2, a3, a4, a5, a6, a7, a8, a9 - ) - else: - return f2 - - -def uncurry10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, TResult]( - f: Callable[ - [T1], - Callable[ - [T2], - Callable[ - [T3], - Callable[ - [T4], - Callable[ - [T5], - Callable[ - [T6], - Callable[ - [T7], - Callable[[T8], Callable[[T9], Callable[[T10], TResult]]], - ], - ], - ], - ], - ], - ], - ], -) -> Callable[[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], TResult]: - def f2( - a1: T1, - a2: T2, - a3: T3, - a4: T4, - a5: T5, - a6: T6, - a7: T7, - a8: T8, - a9: T9, - a10: T10, - ) -> TResult: - return f(a1)(a2)(a3)(a4)(a5)(a6)(a7)(a8)(a9)(a10) - - _curried[f2] = f - return f2 - - -def curry10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, TResult]( - f: Callable[[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], TResult], -) -> Callable[ - [T1], - Callable[ - [T2], - Callable[ - [T3], - Callable[ - [T4], - Callable[ - [T5], - Callable[ - [T6], - Callable[ - [T7], - Callable[[T8], Callable[[T9], Callable[[T10], TResult]]], - ], - ], - ], - ], - ], - ], -]: - f2 = _curried.get(f) - if f2 is None: - return ( - lambda a1: lambda a2: lambda a3: lambda a4: lambda a5: lambda a6: lambda a7: lambda a8: lambda a9: lambda a10: f( - a1, a2, a3, a4, a5, a6, a7, a8, a9, a10 - ) - ) - else: - return f2 - - -def uncurry11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, TResult]( - f: Callable[ - [T1], - Callable[ - [T2], - Callable[ - [T3], - Callable[ - [T4], - Callable[ - [T5], - Callable[ - [T6], - Callable[ - [T7], - Callable[ - [T8], - Callable[ - [T9], - Callable[[T10], Callable[[T11], TResult]], - ], - ], - ], - ], - ], - ], - ], - ], - ], -) -> Callable[[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11], TResult]: - def f2( - a1: Any, - a2: Any, - a3: Any, - a4: Any, - a5: Any, - a6: Any, - a7: Any, - a8: Any, - a9: Any, - a10: Any, - a11: Any, - ) -> Any: - return f(a1)(a2)(a3)(a4)(a5)(a6)(a7)(a8)(a9)(a10)(a11) - - _curried[f2] = f - return f2 - - -def curry11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, TResult]( - f: Callable[[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11], TResult], -) -> Callable[ - [T1], - Callable[ - [T2], - Callable[ - [T3], - Callable[ - [T4], - Callable[ - [T5], - Callable[ - [T6], - Callable[ - [T7], - Callable[ - [T8], - Callable[[T9], Callable[[T10], Callable[[T11], TResult]]], - ], - ], - ], - ], - ], - ], - ], -]: - f2 = _curried.get(f) - if f2 is None: - return ( - lambda a1: lambda a2: lambda a3: lambda a4: lambda a5: lambda a6: lambda a7: lambda a8: lambda a9: lambda a10: lambda a11: f( - a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11 - ) - ) - else: - return f2 - - -def uncurry12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, TResult]( - f: Callable[ - [T1], - Callable[ - [T2], - Callable[ - [T3], - Callable[ - [T4], - Callable[ - [T5], - Callable[ - [T6], - Callable[ - [T7], - Callable[ - [T8], - Callable[ - [T9], - Callable[ - [T10], - Callable[[T11], Callable[[T12], TResult]], - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], -) -> Callable[[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12], TResult]: - def f2( - a1: Any, - a2: Any, - a3: Any, - a4: Any, - a5: Any, - a6: Any, - a7: Any, - a8: Any, - a9: Any, - a10: Any, - a11: Any, - a12: Any, - ) -> Any: - return f(a1)(a2)(a3)(a4)(a5)(a6)(a7)(a8)(a9)(a10)(a11)(a12) - - _curried[f2] = f - return f2 - - -def curry12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, TResult]( - f: Callable[[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12], TResult], -) -> Callable[ - [T1], - Callable[ - [T2], - Callable[ - [T3], - Callable[ - [T4], - Callable[ - [T5], - Callable[ - [T6], - Callable[ - [T7], - Callable[ - [T8], - Callable[ - [T9], - Callable[ - [T10], - Callable[[T11], Callable[[T12], TResult]], - ], - ], - ], - ], - ], - ], - ], - ], - ], -]: - f2 = _curried.get(f) - if f2 is None: - return ( - lambda a1: lambda a2: lambda a3: lambda a4: lambda a5: lambda a6: lambda a7: lambda a8: lambda a9: lambda a10: lambda a11: lambda a12: f( - a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12 - ) - ) - else: - return f2 - - -def uncurry13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, TResult]( - f: Callable[ - [T1], - Callable[ - [T2], - Callable[ - [T3], - Callable[ - [T4], - Callable[ - [T5], - Callable[ - [T6], - Callable[ - [T7], - Callable[ - [T8], - Callable[ - [T9], - Callable[ - [T10], - Callable[ - [T11], - Callable[[T12], Callable[[T13], TResult]], - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], -) -> Callable[[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13], TResult]: - def f2( - a1: Any, - a2: Any, - a3: Any, - a4: Any, - a5: Any, - a6: Any, - a7: Any, - a8: Any, - a9: Any, - a10: Any, - a11: Any, - a12: Any, - a13: Any, - ) -> Any: - return f(a1)(a2)(a3)(a4)(a5)(a6)(a7)(a8)(a9)(a10)(a11)(a12)(a13) - - _curried[f2] = f - return f2 - - -def curry13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, TResult]( - f: Callable[[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13], TResult], -) -> Callable[ - [T1], - Callable[ - [T2], - Callable[ - [T3], - Callable[ - [T4], - Callable[ - [T5], - Callable[ - [T6], - Callable[ - [T7], - Callable[ - [T8], - Callable[ - [T9], - Callable[ - [T10], - Callable[ - [T11], - Callable[[T12], Callable[[T13], TResult]], - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], -]: - f2 = _curried.get(f) - if f2 is None: - return ( - lambda a1: lambda a2: lambda a3: lambda a4: lambda a5: lambda a6: lambda a7: lambda a8: lambda a9: lambda a10: lambda a11: lambda a12: lambda a13: f( - a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13 - ) - ) - else: - return f2 - - -def uncurry14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, TResult]( - f: Callable[ - [T1], - Callable[ - [T2], - Callable[ - [T3], - Callable[ - [T4], - Callable[ - [T5], - Callable[ - [T6], - Callable[ - [T7], - Callable[ - [T8], - Callable[ - [T9], - Callable[ - [T10], - Callable[ - [T11], - Callable[ - [T12], - Callable[ - [T13], - Callable[[T14], TResult], - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], -) -> Callable[ - [ - T1, - T2, - T3, - T4, - T5, - T6, - T7, - T8, - T9, - T10, - T11, - T12, - T13, - T14, - ], - TResult, -]: - def f2( - a1: Any, - a2: Any, - a3: Any, - a4: Any, - a5: Any, - a6: Any, - a7: Any, - a8: Any, - a9: Any, - a10: Any, - a11: Any, - a12: Any, - a13: Any, - a14: Any, - ) -> Any: - return f(a1)(a2)(a3)(a4)(a5)(a6)(a7)(a8)(a9)(a10)(a11)(a12)(a13)(a14) - - _curried[f2] = f - return f2 - - -def curry14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, TResult]( - f: Callable[ - [T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14], - TResult, - ], -) -> Callable[ - [T1], - Callable[ - [T2], - Callable[ - [T3], - Callable[ - [T4], - Callable[ - [T5], - Callable[ - [T6], - Callable[ - [T7], - Callable[ - [T8], - Callable[ - [T9], - Callable[ - [T10], - Callable[ - [T11], - Callable[ - [T12], - Callable[[T13], Callable[[T14], TResult]], - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], -]: - f2 = _curried.get(f) - if f2 is None: - return ( - lambda a1: lambda a2: lambda a3: lambda a4: lambda a5: lambda a6: lambda a7: lambda a8: lambda a9: lambda a10: lambda a11: lambda a12: lambda a13: lambda a14: f( - a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14 - ) - ) - else: - return f2 - - -def uncurry15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, TResult]( - f: Callable[ - [T1], - Callable[ - [T2], - Callable[ - [T3], - Callable[ - [T4], - Callable[ - [T5], - Callable[ - [T6], - Callable[ - [T7], - Callable[ - [T8], - Callable[ - [T9], - Callable[ - [T10], - Callable[ - [T11], - Callable[ - [T12], - Callable[ - [T13], - Callable[ - [T14], - Callable[[T15], TResult], - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], -) -> Callable[ - [T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15], - TResult, -]: - def f2( - a1: Any, - a2: Any, - a3: Any, - a4: Any, - a5: Any, - a6: Any, - a7: Any, - a8: Any, - a9: Any, - a10: Any, - a11: Any, - a12: Any, - a13: Any, - a14: Any, - a15: Any, - ) -> Any: - return f(a1)(a2)(a3)(a4)(a5)(a6)(a7)(a8)(a9)(a10)(a11)(a12)(a13)(a14)(a15) - - _curried[f2] = f - return f2 - - -def curry15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, TResult]( - f: Callable[ - [ - T1, - T2, - T3, - T4, - T5, - T6, - T7, - T8, - T9, - T10, - T11, - T12, - T13, - T14, - T15, - ], - TResult, - ], -) -> Callable[ - [T1], - Callable[ - [T2], - Callable[ - [T3], - Callable[ - [T4], - Callable[ - [T5], - Callable[ - [T6], - Callable[ - [T7], - Callable[ - [T8], - Callable[ - [T9], - Callable[ - [T10], - Callable[ - [T11], - Callable[ - [T12], - Callable[ - [T13], - Callable[ - [T14], - Callable[[T15], TResult], - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], -]: - f2 = _curried.get(f) - if f2 is None: - return ( - lambda a1: lambda a2: lambda a3: lambda a4: lambda a5: lambda a6: lambda a7: lambda a8: lambda a9: lambda a10: lambda a11: lambda a12: lambda a13: lambda a14: lambda a15: f( - a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15 - ) - ) - else: - return f2 - - -def uncurry16[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, TResult]( - f: Callable[ - [T1], - Callable[ - [T2], - Callable[ - [T3], - Callable[ - [T4], - Callable[ - [T5], - Callable[ - [T6], - Callable[ - [T7], - Callable[ - [T8], - Callable[ - [T9], - Callable[ - [T10], - Callable[ - [T11], - Callable[ - [T12], - Callable[ - [T13], - Callable[ - [T14], - Callable[ - [T15], - Callable[[T16], TResult], - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], -) -> Callable[ - [ - T1, - T2, - T3, - T4, - T5, - T6, - T7, - T8, - T9, - T10, - T11, - T12, - T13, - T14, - T15, - T16, - ], - TResult, -]: - def f2( - a1: Any, - a2: Any, - a3: Any, - a4: Any, - a5: Any, - a6: Any, - a7: Any, - a8: Any, - a9: Any, - a10: Any, - a11: Any, - a12: Any, - a13: Any, - a14: Any, - a15: Any, - a16: Any, - ) -> Any: - return f(a1)(a2)(a3)(a4)(a5)(a6)(a7)(a8)(a9)(a10)(a11)(a12)(a13)(a14)(a15)(a16) - - _curried[f2] = f - return f2 - - -def curry16[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, TResult]( - f: Callable[ - [ - T1, - T2, - T3, - T4, - T5, - T6, - T7, - T8, - T9, - T10, - T11, - T12, - T13, - T14, - T15, - T16, - ], - TResult, - ], -) -> Callable[ - [T1], - Callable[ - [T2], - Callable[ - [T3], - Callable[ - [T4], - Callable[ - [T5], - Callable[ - [T6], - Callable[ - [T7], - Callable[ - [T8], - Callable[ - [T9], - Callable[ - [T10], - Callable[ - [T11], - Callable[ - [T12], - Callable[ - [T13], - Callable[ - [T14], - Callable[ - [T15], - Callable[[T16], TResult], - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], -]: - f2 = _curried.get(f) - if f2 is None: - return ( - lambda a1: lambda a2: lambda a3: lambda a4: lambda a5: lambda a6: lambda a7: lambda a8: lambda a9: lambda a10: lambda a11: lambda a12: lambda a13: lambda a14: lambda a15: lambda a16: f( - a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16 - ) - ) - else: - return f2 - - -def uncurry17[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, TResult]( - f: Callable[ - [T1], - Callable[ - [T2], - Callable[ - [T3], - Callable[ - [T4], - Callable[ - [T5], - Callable[ - [T6], - Callable[ - [T7], - Callable[ - [T8], - Callable[ - [T9], - Callable[ - [T10], - Callable[ - [T11], - Callable[ - [T12], - Callable[ - [T13], - Callable[ - [T14], - Callable[ - [T15], - Callable[ - [T16], - Callable[[T17], TResult], - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], -) -> Callable[ - [ - T1, - T2, - T3, - T4, - T5, - T6, - T7, - T8, - T9, - T10, - T11, - T12, - T13, - T14, - T15, - T16, - T17, - ], - TResult, -]: - def f2( - a1: Any, - a2: Any, - a3: Any, - a4: Any, - a5: Any, - a6: Any, - a7: Any, - a8: Any, - a9: Any, - a10: Any, - a11: Any, - a12: Any, - a13: Any, - a14: Any, - a15: Any, - a16: Any, - a17: Any, - ) -> Any: - return f(a1)(a2)(a3)(a4)(a5)(a6)(a7)(a8)(a9)(a10)(a11)(a12)(a13)(a14)(a15)(a16)(a17) - - _curried[f2] = f - return f2 - - -def curry17[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, TResult]( - f: Callable[ - [ - T1, - T2, - T3, - T4, - T5, - T6, - T7, - T8, - T9, - T10, - T11, - T12, - T13, - T14, - T15, - T16, - T17, - ], - TResult, - ], -) -> Callable[ - [T1], - Callable[ - [T2], - Callable[ - [T3], - Callable[ - [T4], - Callable[ - [T5], - Callable[ - [T6], - Callable[ - [T7], - Callable[ - [T8], - Callable[ - [T9], - Callable[ - [T10], - Callable[ - [T11], - Callable[ - [T12], - Callable[ - [T13], - Callable[ - [T14], - Callable[ - [T15], - Callable[ - [T16], - Callable[[T17], TResult], - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], -]: - f2 = _curried.get(f) - if f2 is None: - return ( - lambda a1: lambda a2: lambda a3: lambda a4: lambda a5: lambda a6: lambda a7: lambda a8: lambda a9: lambda a10: lambda a11: lambda a12: lambda a13: lambda a14: lambda a15: lambda a16: lambda a17: f( - a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17 - ) - ) - else: - return f2 - - -def uncurry18[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, TResult]( - f: Callable[ - [T1], - Callable[ - [T2], - Callable[ - [T3], - Callable[ - [T4], - Callable[ - [T5], - Callable[ - [T6], - Callable[ - [T7], - Callable[ - [T8], - Callable[ - [T9], - Callable[ - [T10], - Callable[ - [T11], - Callable[ - [T12], - Callable[ - [T13], - Callable[ - [T14], - Callable[ - [T15], - Callable[ - [T16], - Callable[ - [T17], - Callable[ - [T18], - TResult, - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], -) -> Callable[ - [ - T1, - T2, - T3, - T4, - T5, - T6, - T7, - T8, - T9, - T10, - T11, - T12, - T13, - T14, - T15, - T16, - T17, - T18, - ], - TResult, -]: - def f2( - a1: Any, - a2: Any, - a3: Any, - a4: Any, - a5: Any, - a6: Any, - a7: Any, - a8: Any, - a9: Any, - a10: Any, - a11: Any, - a12: Any, - a13: Any, - a14: Any, - a15: Any, - a16: Any, - a17: Any, - a18: Any, - ) -> Any: - return f(a1)(a2)(a3)(a4)(a5)(a6)(a7)(a8)(a9)(a10)(a11)(a12)(a13)(a14)(a15)(a16)(a17)(a18) - - _curried[f2] = f - return f2 - - -def curry18[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, TResult]( - f: Callable[ - [ - T1, - T2, - T3, - T4, - T5, - T6, - T7, - T8, - T9, - T10, - T11, - T12, - T13, - T14, - T15, - T16, - T17, - T18, - ], - TResult, - ], -) -> Callable[ - [T1], - Callable[ - [T2], - Callable[ - [T3], - Callable[ - [T4], - Callable[ - [T5], - Callable[ - [T6], - Callable[ - [T7], - Callable[ - [T8], - Callable[ - [T9], - Callable[ - [T10], - Callable[ - [T11], - Callable[ - [T12], - Callable[ - [T13], - Callable[ - [T14], - Callable[ - [T15], - Callable[ - [T16], - Callable[ - [T17], - Callable[[T18], TResult], - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], -]: - f2 = _curried.get(f) - if f2 is None: - return ( - lambda a1: lambda a2: lambda a3: lambda a4: lambda a5: lambda a6: lambda a7: lambda a8: lambda a9: lambda a10: lambda a11: lambda a12: lambda a13: lambda a14: lambda a15: lambda a16: lambda a17: lambda a18: f( - a1, - a2, - a3, - a4, - a5, - a6, - a7, - a8, - a9, - a10, - a11, - a12, - a13, - a14, - a15, - a16, - a17, - a18, - ) - ) - else: - return f2 - - -def uncurry19[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, TResult]( - f: Callable[ - [T1], - Callable[ - [T2], - Callable[ - [T3], - Callable[ - [T4], - Callable[ - [T5], - Callable[ - [T6], - Callable[ - [T7], - Callable[ - [T8], - Callable[ - [T9], - Callable[ - [T10], - Callable[ - [T11], - Callable[ - [T12], - Callable[ - [T13], - Callable[ - [T14], - Callable[ - [T15], - Callable[ - [T16], - Callable[ - [T17], - Callable[ - [T18], - Callable[ - [T19], - TResult, - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], -) -> Callable[ - [ - T1, - T2, - T3, - T4, - T5, - T6, - T7, - T8, - T9, - T10, - T11, - T12, - T13, - T14, - T15, - T16, - T17, - T18, - T19, - ], - TResult, -]: - def f2( - a1: Any, - a2: Any, - a3: Any, - a4: Any, - a5: Any, - a6: Any, - a7: Any, - a8: Any, - a9: Any, - a10: Any, - a11: Any, - a12: Any, - a13: Any, - a14: Any, - a15: Any, - a16: Any, - a17: Any, - a18: Any, - a19: Any, - ) -> Any: - return f(a1)(a2)(a3)(a4)(a5)(a6)(a7)(a8)(a9)(a10)(a11)(a12)(a13)(a14)(a15)(a16)(a17)(a18)(a19) - - _curried[f2] = f - return f2 - - -def curry19[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, TResult]( - f: Callable[ - [ - T1, - T2, - T3, - T4, - T5, - T6, - T7, - T8, - T9, - T10, - T11, - T12, - T13, - T14, - T15, - T16, - T17, - T18, - T19, - ], - TResult, - ], -) -> Callable[ - [T1], - Callable[ - [T2], - Callable[ - [T3], - Callable[ - [T4], - Callable[ - [T5], - Callable[ - [T6], - Callable[ - [T7], - Callable[ - [T8], - Callable[ - [T9], - Callable[ - [T10], - Callable[ - [T11], - Callable[ - [T12], - Callable[ - [T13], - Callable[ - [T14], - Callable[ - [T15], - Callable[ - [T16], - Callable[ - [T17], - Callable[ - [T18], - Callable[ - [T19], - TResult, - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], -]: - f2 = _curried.get(f) - if f2 is None: - return ( - lambda a1: lambda a2: lambda a3: lambda a4: lambda a5: lambda a6: lambda a7: lambda a8: lambda a9: lambda a10: lambda a11: lambda a12: lambda a13: lambda a14: lambda a15: lambda a16: lambda a17: lambda a18: lambda a19: f( - a1, - a2, - a3, - a4, - a5, - a6, - a7, - a8, - a9, - a10, - a11, - a12, - a13, - a14, - a15, - a16, - a17, - a18, - a19, - ) - ) - else: - return f2 - - -def uncurry20[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, TResult]( - f: Callable[ - [T1], - Callable[ - [T2], - Callable[ - [T3], - Callable[ - [T4], - Callable[ - [T5], - Callable[ - [T6], - Callable[ - [T7], - Callable[ - [T8], - Callable[ - [T9], - Callable[ - [T10], - Callable[ - [T11], - Callable[ - [T12], - Callable[ - [T13], - Callable[ - [T14], - Callable[ - [T15], - Callable[ - [T16], - Callable[ - [T17], - Callable[ - [T18], - Callable[ - [T19], - Callable[ - [T20], - TResult, - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], -) -> Callable[ - [ - T1, - T2, - T3, - T4, - T5, - T6, - T7, - T8, - T9, - T10, - T11, - T12, - T13, - T14, - T15, - T16, - T17, - T18, - T19, - T20, - ], - TResult, -]: - def f2( - a1: Any, - a2: Any, - a3: Any, - a4: Any, - a5: Any, - a6: Any, - a7: Any, - a8: Any, - a9: Any, - a10: Any, - a11: Any, - a12: Any, - a13: Any, - a14: Any, - a15: Any, - a16: Any, - a17: Any, - a18: Any, - a19: Any, - a20: Any, - ) -> Any: - return f(a1)(a2)(a3)(a4)(a5)(a6)(a7)(a8)(a9)(a10)(a11)(a12)(a13)(a14)(a15)(a16)(a17)(a18)(a19)(a20) - - _curried[f2] = f - return f2 - - -def curry20[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, TResult]( - f: Callable[ - [ - T1, - T2, - T3, - T4, - T5, - T6, - T7, - T8, - T9, - T10, - T11, - T12, - T13, - T14, - T15, - T16, - T17, - T18, - T19, - T20, - ], - TResult, - ], -) -> Callable[ - [T1], - Callable[ - [T2], - Callable[ - [T3], - Callable[ - [T4], - Callable[ - [T5], - Callable[ - [T6], - Callable[ - [T7], - Callable[ - [T8], - Callable[ - [T9], - Callable[ - [T10], - Callable[ - [T11], - Callable[ - [T12], - Callable[ - [T13], - Callable[ - [T14], - Callable[ - [T15], - Callable[ - [T16], - Callable[ - [T17], - Callable[ - [T18], - Callable[ - [T19], - Callable[ - [T20], - TResult, - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], - ], -]: - f2 = _curried.get(f) - if f2 is None: - return ( - lambda a1: lambda a2: lambda a3: lambda a4: lambda a5: lambda a6: lambda a7: lambda a8: lambda a9: lambda a10: lambda a11: lambda a12: lambda a13: lambda a14: lambda a15: lambda a16: lambda a17: lambda a18: lambda a19: lambda a20: f( - a1, - a2, - a3, - a4, - a5, - a6, - a7, - a8, - a9, - a10, - a11, - a12, - a13, - a14, - a15, - a16, - a17, - a18, - a19, - a20, - ) - ) - else: - return f2 - - def is_array_like(x: Any) -> TypeGuard[Array]: # Match FSharpArray (Rust) which has .length property # Also match tuples for F# tuple pattern matching @@ -2791,6 +858,7 @@ def range(start: int, stop: int, step: int = 1) -> Iterable[int32]: __all__ = [ + "UNIT", "AnonymousDisposable", "ComparableBase", "Disposable", @@ -2809,17 +877,9 @@ def range(start: int, stop: int, step: int = 1) -> Iterable[int32]: "StaticPropertyBase", "StaticPropertyMeta", "StringableBase", + "Unit", "array_hash", "copy_to_array", - "curry2", - "curry3", - "curry4", - "curry5", - "curry6", - "curry7", - "curry8", - "curry9", - "curry10", "escape_data_string", "escape_uri_string", "get_platform", @@ -2832,14 +892,5 @@ def range(start: int, stop: int, step: int = 1) -> Iterable[int32]: "range", "round", "to_iterable", - "uncurry2", - "uncurry3", - "uncurry4", - "uncurry5", - "uncurry6", - "uncurry7", - "uncurry8", - "uncurry9", - "uncurry10", "unescape_data_string", ]