diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3e2920024f..3c65091a58 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -33,6 +33,22 @@ jobs: - name: Check F# code run: dotnet fantomas . --check --verbosity d + - name: Set up Python 3.13 + uses: actions/setup-python@v7 + with: + python-version: "3.13" + + - name: Set up uv + uses: astral-sh/setup-uv@v7 + with: + enable-cache: true + + - name: Check Python imports and exports + run: uv run --frozen ruff check --select I,RUF022,RUF068 src/fable-library-py + + - name: Check Python formatting + run: uv run --frozen ruff format --check src/fable-library-py + # Verify that Fable.sln can be restored # See: https://github.com/fable-compiler/Fable/issues/3687 restore-project: diff --git a/src/fable-library-py/fable_library/array_.py b/src/fable-library-py/fable_library/array_.py index 72ce5665ef..2b8877c24d 100644 --- a/src/fable-library-py/fable_library/array_.py +++ b/src/fable-library-py/fable_library/array_.py @@ -196,6 +196,7 @@ "UInt64ArrayCons", "add_in_place", "add_range_in_place", + "all_pairs", "append", "average", "average_by", @@ -211,6 +212,7 @@ "create", "empty", "equals_with", + "exactly_one", "exists", "exists2", "exists_offset", @@ -261,8 +263,6 @@ "partition", "permute", "pick", - "reduce", - "reduce_back", "random_choice", "random_choice_by", "random_choice_with", @@ -278,6 +278,8 @@ "random_shuffle_in_place_by", "random_shuffle_in_place_with", "random_shuffle_with", + "reduce", + "reduce_back", "remove_all_in_place", "remove_at", "remove_in_place", @@ -301,8 +303,6 @@ "split_into", "sum", "sum_by", - "all_pairs", - "exactly_one", "tail", "take", "take_while", @@ -320,8 +320,8 @@ "unzip", "unzip3", "update_at", - "windowed", "where", + "windowed", "zero_create", "zip", "zip3", diff --git a/src/fable-library-py/fable_library/async_builder.py b/src/fable-library-py/fable_library/async_builder.py index e3f43ead41..c65ee3e558 100644 --- a/src/fable-library-py/fable_library/async_builder.py +++ b/src/fable-library-py/fable_library/async_builder.py @@ -456,3 +456,23 @@ def Zero(self) -> Async[None]: singleton = AsyncBuilder() + + +__all__ = [ + "AnonymousAsyncContext", + "Async", + "AsyncBuilder", + "CancellationCallbackError", + "CancellationToken", + "Continuations", + "IAsyncContext", + "OperationCanceledError", + "ScheduledItem", + "TaskCanceledError", + "Trampoline", + "empty_continuation", + "protected_bind", + "protected_cont", + "protected_return", + "singleton", +] diff --git a/src/fable-library-py/fable_library/char.py b/src/fable-library-py/fable_library/char.py index 9359054d4a..9be399678e 100644 --- a/src/fable-library-py/fable_library/char.py +++ b/src/fable-library-py/fable_library/char.py @@ -239,7 +239,6 @@ def parse(input: str) -> str: "is_number", "is_punctuation", "is_separator", - "is_separator", "is_surrogate", "is_surrogate_pair", "is_symbol", diff --git a/src/fable-library-py/fable_library/core/__init__.py b/src/fable-library-py/fable_library/core/__init__.py index 042ca692fa..66db388c1e 100644 --- a/src/fable-library-py/fable_library/core/__init__.py +++ b/src/fable-library-py/fable_library/core/__init__.py @@ -1,4 +1,7 @@ -from math import copysign as _copysign, fmod as _fmod, inf as _inf, nan as _nan +from math import copysign as _copysign +from math import fmod as _fmod +from math import inf as _inf +from math import nan as _nan from typing import Literal, SupportsFloat, SupportsInt from ._core import ( diff --git a/src/fable-library-py/fable_library/diagnostics.py b/src/fable-library-py/fable_library/diagnostics.py index 03c78829ca..fbe00a6a4e 100644 --- a/src/fable-library-py/fable_library/diagnostics.py +++ b/src/fable-library-py/fable_library/diagnostics.py @@ -54,3 +54,11 @@ def frequency() -> int: def get_timestamp() -> int64: return int64(time.perf_counter_ns()) + + +__all__ = [ + "StopWatch", + "TimerError", + "frequency", + "get_timestamp", +] diff --git a/src/fable-library-py/fable_library/event.py b/src/fable-library-py/fable_library/event.py index 115f2e7f7f..26aebcd35b 100644 --- a/src/fable-library-py/fable_library/event.py +++ b/src/fable-library-py/fable_library/event.py @@ -217,3 +217,25 @@ def create_event[T]( remove_handler: Callable[[DotNetDelegate[T]], None], ) -> IEvent[T]: return AnonymousEvent[T](add_handler, remove_handler) + + +__all__ = [ + "AnonymousEvent", + "Delegate", + "DotNetDelegate", + "Event", + "EventDelegate", + "IDelegateEvent", + "IEvent", + "IEvent_2", + "add", + "choose", + "create_event", + "filter", + "map", + "merge", + "pairwise", + "partition", + "scan", + "split", +] diff --git a/src/fable-library-py/fable_library/exception_bases.py b/src/fable-library-py/fable_library/exception_bases.py index 6e66ff5c20..817c257320 100644 --- a/src/fable-library-py/fable_library/exception_bases.py +++ b/src/fable-library-py/fable_library/exception_bases.py @@ -50,3 +50,16 @@ class MemoryErrorBase(ExceptionBase, MemoryError): class TimeoutErrorBase(ExceptionBase, TimeoutError): """A timeout error with .NET message and inner-exception support.""" + + +__all__ = [ + "ExceptionBase", + "IndexErrorBase", + "MemoryErrorBase", + "NotImplementedErrorBase", + "OverflowErrorBase", + "RuntimeErrorBase", + "TimeoutErrorBase", + "ValueErrorBase", + "ZeroDivisionErrorBase", +] diff --git a/src/fable-library-py/fable_library/file.py b/src/fable-library-py/fable_library/file.py index b2fb8b6ea3..dced0abab9 100644 --- a/src/fable-library-py/fable_library/file.py +++ b/src/fable-library-py/fable_library/file.py @@ -107,3 +107,24 @@ def replace(source_file_name: str, destination_file_name: str, destination_backu with open(destination_backup_file_name, "wb") as f: f.write(data) os.replace(source_file_name, destination_file_name) + + +__all__ = [ + "copy", + "delete", + "exists", + "move", + "read_all_bytes", + "read_all_bytes_async", + "read_all_lines", + "read_all_lines_async", + "read_all_text", + "read_all_text_async", + "replace", + "write_all_bytes", + "write_all_bytes_async", + "write_all_lines", + "write_all_lines_async", + "write_all_text", + "write_all_text_async", +] diff --git a/src/fable-library-py/fable_library/long.py b/src/fable-library-py/fable_library/long.py index 90febfa89f..0936936fbf 100644 --- a/src/fable-library-py/fable_library/long.py +++ b/src/fable-library-py/fable_library/long.py @@ -304,54 +304,29 @@ def to_string(x: int64 | uint64) -> str: "from_value", "get_range", "max", - "max", - "min", "min", "op_addition", - "op_addition", - "op_bitwise_and", "op_bitwise_and", "op_bitwise_or", - "op_bitwise_or", - "op_division", "op_division", "op_equality", - "op_equality", "op_exclusive_or", - "op_exclusive_or", - "op_greater_than", "op_greater_than", "op_greater_than_or_equal", - "op_greater_than_or_equal", "op_inequality", - "op_inequality", - "op_left_shift", "op_left_shift", "op_less_than", - "op_less_than", "op_less_than_or_equal", - "op_less_than_or_equal", - "op_logical_not", "op_logical_not", "op_modulus", - "op_modulus", "op_multiply", - "op_multiply", - "op_right_shift", "op_right_shift", "op_subtraction", - "op_subtraction", "op_unary_negation", - "op_unary_negation", - "op_unary_plus", "op_unary_plus", "parse", - "parse", - "sign", "sign", "to_number", "to_string", - "to_string", - "try_parse", "try_parse", ] diff --git a/src/fable-library-py/fable_library/map_util.py b/src/fable-library-py/fable_library/map_util.py index d33794bb5b..69ea4909a6 100644 --- a/src/fable-library-py/fable_library/map_util.py +++ b/src/fable-library-py/fable_library/map_util.py @@ -272,3 +272,31 @@ def assign(key: str, case_rule: CaseRules, value: Any): fail(kv_pair) return obj + + +__all__ = [ + "CaseRules", + "add_to_dict", + "add_to_set", + "change_case", + "contains_in_set", + "contains_value", + "copy_to_array_from_set", + "dashify", + "except_with_set", + "get_item_from_dict", + "intersect_with_set", + "is_proper_subset_of_set", + "is_proper_superset_of_set", + "is_subset_of_set", + "is_superset_of_set", + "key_value_list", + "make_dict", + "overlaps_with_set", + "remove_from_dict", + "remove_from_set", + "set_equals_set", + "symmetric_except_with_set", + "try_get_value", + "union_with_set", +] diff --git a/src/fable-library-py/fable_library/observable.py b/src/fable-library-py/fable_library/observable.py index 9c5cef70ae..7cb80d1fe1 100644 --- a/src/fable-library-py/fable_library/observable.py +++ b/src/fable-library-py/fable_library/observable.py @@ -216,3 +216,22 @@ def split[T, U, V]( choose(lambda v: Choice_tryValueIfChoice1Of2(splitter(v)), source), choose(lambda v: Choice_tryValueIfChoice2Of2(splitter(v)), source), ) + + +__all__ = [ + "IObservable", + "IObserver", + "Observable", + "Observer", + "add", + "choose", + "filter", + "map", + "merge", + "pairwise", + "partition", + "protect", + "scan", + "split", + "subscribe", +] diff --git a/src/fable-library-py/fable_library/protocols.py b/src/fable-library-py/fable_library/protocols.py index 49bd980945..a123a17f49 100644 --- a/src/fable-library-py/fable_library/protocols.py +++ b/src/fable-library-py/fable_library/protocols.py @@ -442,3 +442,31 @@ def keys(self, __unit: Unit = None) -> Iterable[K]: def __getitem__(self, key: K, /) -> V: """Get the value for a key.""" ... + + +__all__ = [ + "HashCode", + "ICollection", + "IComparable", + "IComparable_1", + "IComparer", + "IComparer_1", + "IDictionary_2", + "IDisposable", + "IEnumerable", + "IEnumerable_1", + "IEnumerator", + "IEqualityComparer", + "IEqualityComparer_1", + "IEquatable", + "IGenericAdder", + "IGenericAverager", + "IReadOnlyCollection_1", + "IReadOnlyDictionary", + "IReadOnlyDictionary_2", + "IReadOnlySet_1", + "ISet", + "IStructuralComparable", + "IStructuralEquatable", + "SupportsLessThan", +] diff --git a/src/fable-library-py/fable_library/quotation.py b/src/fable-library-py/fable_library/quotation.py index 8f2a821dd3..df41ab4a0a 100644 --- a/src/fable-library-py/fable_library/quotation.py +++ b/src/fable-library-py/fable_library/quotation.py @@ -698,3 +698,72 @@ def is_specific_call(template: Expr, expr: Expr) -> tuple[Expr | None, FSharpLis # names rather than System.Type, so it is always empty; callers match it with # a wildcard. return (instance, of_array(Array([])), of_array(expr.args)) + + +__all__ = [ + "Expr", + "ExprApplication", + "ExprCall", + "ExprFieldGet", + "ExprFieldSet", + "ExprIfThenElse", + "ExprLambda", + "ExprLet", + "ExprNewList", + "ExprNewRecord", + "ExprNewTuple", + "ExprNewUnion", + "ExprSequential", + "ExprTupleGet", + "ExprUnionField", + "ExprUnionTag", + "ExprValue", + "ExprVarExpr", + "ExprVarSet", + "Var", + "call_declaring_type", + "evaluate", + "expr_to_string", + "get_free_vars", + "get_type", + "is_and_also", + "is_application", + "is_call", + "is_field_get", + "is_if_then_else", + "is_lambda", + "is_let", + "is_new_record", + "is_new_tuple", + "is_new_union_case", + "is_or_else", + "is_sequential", + "is_specific_call", + "is_tuple_get", + "is_value", + "is_var", + "mk_application", + "mk_call", + "mk_field_get", + "mk_field_set", + "mk_if_then_else", + "mk_lambda", + "mk_let", + "mk_new_list", + "mk_new_record", + "mk_new_tuple", + "mk_new_union", + "mk_null", + "mk_quot_var", + "mk_sequential", + "mk_tuple_get", + "mk_union_field", + "mk_union_tag", + "mk_value", + "mk_var", + "mk_var_set", + "substitute", + "var_get_is_mutable", + "var_get_name", + "var_get_type", +] diff --git a/src/fable-library-py/fable_library/reflection.py b/src/fable-library-py/fable_library/reflection.py index 071b0636d8..b9e1fef101 100644 --- a/src/fable-library-py/fable_library/reflection.py +++ b/src/fable-library-py/fable_library/reflection.py @@ -500,3 +500,90 @@ def get_case_name(x: Any) -> str: def get_case_fields(x: Any) -> list[Any]: assert_union(x) return x.fields + + +__all__ = [ + "CaseInfo", + "Constructor", + "EnumCase", + "FieldInfo", + "MethodInfo", + "ParameterInfo", + "PropertyInfo", + "TypeInfo", + "anon_record_type", + "array_type", + "assert_union", + "bigint_type", + "bool_type", + "char_type", + "class_type", + "create_instance", + "decimal_type", + "delegate_type", + "enum_type", + "equals", + "float32_type", + "float64_type", + "full_name", + "get_case_fields", + "get_case_name", + "get_case_tag", + "get_element_type", + "get_enum_case", + "get_enum_name", + "get_enum_names", + "get_enum_underlying_type", + "get_enum_values", + "get_function_elements", + "get_generic_type_definition", + "get_generics", + "get_record_elements", + "get_record_field", + "get_record_fields", + "get_tuple_elements", + "get_tuple_field", + "get_tuple_fields", + "get_union_case_fields", + "get_union_cases", + "get_union_fields", + "get_value", + "int8_type", + "int16_type", + "int32_type", + "int64_type", + "is_array", + "is_enum", + "is_enum_defined", + "is_erased_to_number", + "is_function", + "is_generic_type", + "is_instance_of_type", + "is_record", + "is_subclass_of", + "is_tuple", + "is_union", + "lambda_type", + "list_type", + "make_generic_type", + "make_record", + "make_tuple", + "make_union", + "name", + "namespace", + "nativeint_type", + "obj_type", + "option_type", + "parse_enum", + "record_type", + "string_type", + "try_parse_enum", + "tuple_type", + "uint8_type", + "uint16_type", + "uint32_type", + "uint64_type", + "unativeint_type", + "union_type", + "unit_type", +] diff --git a/src/fable-library-py/fable_library/seq_native.py b/src/fable-library-py/fable_library/seq_native.py index 1e2342442b..335ba69ebe 100644 --- a/src/fable-library-py/fable_library/seq_native.py +++ b/src/fable-library-py/fable_library/seq_native.py @@ -29,6 +29,7 @@ from .types import UNIT, Unit from .util import Enumerator, get_enumerator, to_iterator + _SENTINEL: Any = object() diff --git a/src/fable-library-py/fable_library/thread.py b/src/fable-library-py/fable_library/thread.py index b704c56ba4..4f0a17b78f 100644 --- a/src/fable-library-py/fable_library/thread.py +++ b/src/fable-library-py/fable_library/thread.py @@ -6,3 +6,8 @@ def sleep(milliseconds: int | TimeSpan) -> None: milliseconds_value = to_milliseconds(milliseconds) time.sleep(milliseconds_value / 1000.0) + + +__all__ = [ + "sleep", +] diff --git a/src/fable-library-py/fable_library/time_span.py b/src/fable-library-py/fable_library/time_span.py index 3467ce29ad..3e5de67fde 100644 --- a/src/fable-library-py/fable_library/time_span.py +++ b/src/fable-library-py/fable_library/time_span.py @@ -340,14 +340,10 @@ def to_milliseconds(value: int | TimeSpan) -> float: "subtract", "to_milliseconds", "total_days", - "total_days", - "total_hours", "total_hours", "total_microseconds", "total_milliseconds", "total_minutes", - "total_minutes", - "total_seconds", "total_seconds", "try_parse", ] diff --git a/src/fable-library-py/fable_library/uri.py b/src/fable-library-py/fable_library/uri.py index 1b71567eb5..1925728356 100644 --- a/src/fable-library-py/fable_library/uri.py +++ b/src/fable-library-py/fable_library/uri.py @@ -108,3 +108,9 @@ def try_create(uri: str | Uri, kind_or_uri: UriKind | Uri | str | int, out: FSha return True except Exception: return False + + +__all__ = [ + "Uri", + "UriKind", +] diff --git a/src/fable-library-py/fable_library/util.py b/src/fable-library-py/fable_library/util.py index a808660b3b..e4aaf03d4c 100644 --- a/src/fable-library-py/fable_library/util.py +++ b/src/fable-library-py/fable_library/util.py @@ -967,19 +967,19 @@ def range(start: int, stop: int, step: int = 1) -> Iterable[int]: "Unit", "array_hash", "copy_to_array", + "create_random", "erase_unit_callable", "escape_data_string", "escape_uri_string", - "create_random", "get_platform", "identity_hash", "ignore", "nullable", "number_hash", "physical_hash", - "random_int", "random_bytes", "random_double", + "random_int", "range", "round", "to_iterable", diff --git a/src/fable-library-py/tests/test_ints.py b/src/fable-library-py/tests/test_ints.py index 828de2a5b0..cb30093192 100644 --- a/src/fable-library-py/tests/test_ints.py +++ b/src/fable-library-py/tests/test_ints.py @@ -2,7 +2,6 @@ from decimal import Decimal import pytest - from fable_library.core import FSharpRef, Int32, byte, float32, int16, int32, int64, sbyte, uint16, uint32, uint64 from fable_library.int32 import parse as int32_parse from fable_library.int32 import try_parse as int32_try_parse