Skip to content

Commit

Permalink
Merge pull request #3554 from fable-compiler/python-fixes-oct-19
Browse files Browse the repository at this point in the history
[Python[ Change python requirement to >= 3.10
  • Loading branch information
dbrattli authored Oct 19, 2023
2 parents 7b51c59 + fa4ce31 commit 61437aa
Show file tree
Hide file tree
Showing 22 changed files with 623 additions and 380 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [3.9, "3.10", 3.11]
python-version: ["3.10", 3.11, 3.12]

steps:
- uses: actions/checkout@v2
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "Fable"
version = "4.0.0"
version = "4.3.0"
description = "Fable"
authors = ["Alfonso Garcia-Caro <@alfonsogcnunez>", "Dag Brattli <dag@brattli.net>"]
license = "MIT License"
Expand Down
2 changes: 1 addition & 1 deletion src/Fable.Transforms/Python/Fable2Python.fs
Original file line number Diff line number Diff line change
Expand Up @@ -727,7 +727,7 @@ module Annotation =
stdlibModuleTypeHint com ctx "typing" "Callable" (argTypes @ [ returnType ])
| Fable.DelegateType (argTypes, returnType) -> stdlibModuleTypeHint com ctx "typing" "Callable" (argTypes @ [ returnType ])
| Fable.Option (genArg, _) -> stdlibModuleTypeHint com ctx "typing" "Optional" [ genArg ]
| Fable.Tuple (genArgs, _) -> stdlibModuleTypeHint com ctx "typing" "Tuple" genArgs
| Fable.Tuple (genArgs, _) -> makeGenericTypeAnnotation com ctx "tuple" genArgs None, []
| Fable.Array (genArg, _) ->
match genArg with
| Fable.Type.Number (UInt8, _) -> Expression.name "bytearray", []
Expand Down
20 changes: 10 additions & 10 deletions src/fable-library-py/fable_library/bit_converter.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,43 +5,43 @@


def get_bytes_char(value: str) -> bytes:
return bytearray(bytes(value, "UTF-8"))
return bytes(value, "UTF-8")


def get_bytes_int16(value: int) -> bytes:
return bytearray(value.to_bytes(length=2, byteorder=sys.byteorder))
return value.to_bytes(length=2, byteorder=sys.byteorder)


def get_bytes_uint16(value: int) -> bytes:
return bytearray(value.to_bytes(length=2, byteorder=sys.byteorder))
return value.to_bytes(length=2, byteorder=sys.byteorder)


def get_bytes_int32(value: int) -> bytes:
return bytearray(value.to_bytes(length=4, byteorder=sys.byteorder))
return value.to_bytes(length=4, byteorder=sys.byteorder)


def get_bytes_uint32(value: int) -> bytes:
return bytearray(value.to_bytes(length=4, byteorder=sys.byteorder))
return value.to_bytes(length=4, byteorder=sys.byteorder)


def get_bytes_int64(value: int) -> bytes:
return bytearray(value.to_bytes(length=8, byteorder=sys.byteorder))
return value.to_bytes(length=8, byteorder=sys.byteorder)


def get_bytes_uint64(value: int) -> bytes:
return bytearray(value.to_bytes(length=8, byteorder=sys.byteorder))
return value.to_bytes(length=8, byteorder=sys.byteorder)


def get_bytes_boolean(value: bool) -> bytes:
return bytearray(value.to_bytes(length=1, byteorder=sys.byteorder))
return value.to_bytes(length=1, byteorder=sys.byteorder)


def get_bytes_single(value: float) -> bytes:
return bytearray(struct.pack("f", value))
return struct.pack("f", value)


def get_bytes_double(value: float) -> bytes:
return bytearray(struct.pack("d", value))
return struct.pack("d", value)


def int64bits_to_double(value: int) -> float:
Expand Down
8 changes: 4 additions & 4 deletions src/fable-library-py/fable_library/char.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import unicodedata
from __future__ import annotations

import unicodedata
from enum import IntEnum
from typing import Dict, Union


class UnicodeCategory(IntEnum):
Expand Down Expand Up @@ -42,7 +42,7 @@ class UnicodeCategory(IntEnum):
OtherNotAssigned = 29


unicode_category_2_python: Dict[str, UnicodeCategory] = {
unicode_category_2_python: dict[str, UnicodeCategory] = {
"Ll": UnicodeCategory.LowercaseLetter,
"Lu": UnicodeCategory.UppercaseLetter,
"Nd": UnicodeCategory.DecimalDigitNumber,
Expand Down Expand Up @@ -203,7 +203,7 @@ def is_surrogate(s: str, index: int = 0) -> bool:
return 0xD800 <= cp <= 0xDFFF


def is_surrogate_pair(s: str, index: Union[str, int]) -> bool:
def is_surrogate_pair(s: str, index: str | int) -> bool:
if isinstance(index, int):
return is_high_surrogate(s, index) and is_low_surrogate(s, index + 1)

Expand Down
11 changes: 5 additions & 6 deletions src/fable-library-py/fable_library/date_offset.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from __future__ import annotations

from datetime import datetime, timedelta, timezone
from typing import Optional, Union

from .types import FSharpRef

Expand Down Expand Up @@ -31,8 +32,8 @@ def create(
h: int,
m: int,
s: int,
ms: Union[int, timedelta],
offset: Optional[timedelta] = None,
ms: int | timedelta,
offset: timedelta | None = None,
) -> datetime:
if isinstance(ms, timedelta):
offset = ms
Expand All @@ -57,9 +58,7 @@ def op_addition(x: datetime, y: timedelta) -> datetime:
return x + y


def op_subtraction(
x: datetime, y: Union[datetime, timedelta]
) -> Union[datetime, timedelta]:
def op_subtraction(x: datetime, y: datetime | timedelta) -> datetime | timedelta:
if isinstance(y, timedelta):
return x - y

Expand Down
16 changes: 9 additions & 7 deletions src/fable-library-py/fable_library/diagnostics.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from __future__ import annotations

import time
from typing import Optional


class TimerError(Exception):
Expand All @@ -8,8 +9,8 @@ class TimerError(Exception):

class StopWatch:
def __init__(self) -> None:
self._start_time: Optional[float] = None
self._elapsed_time: Optional[float] = None
self._start_time: float | None = None
self._elapsed_time: float | None = None

def is_running(self) -> bool:
"""Return True if timer is running"""
Expand All @@ -18,32 +19,33 @@ def is_running(self) -> bool:
def start(self) -> None:
"""Start a new timer"""
if self._start_time is not None:
raise TimerError(f"Timer is running. Use .stop() to stop it")
raise TimerError("Timer is running. Use .stop() to stop it")

self._start_time = time.perf_counter()

def stop(self) -> None:
"""Stop the timer, and report the elapsed time"""
if self._start_time is None:
raise TimerError(f"Timer is not running. Use .start() to start it")
raise TimerError("Timer is not running. Use .start() to start it")

self._elapsed_time = time.perf_counter() - self._start_time
self._start_time = None

def elapsed_milliseconds(self) -> int:
"""Return the elapsed time in milliseconds"""
if self._elapsed_time is None:
raise TimerError(f"Timer is not running. Use .start() to start it")
raise TimerError("Timer is not running. Use .start() to start it")

return int(self._elapsed_time * 1000)

def elapsed_ticks(self) -> int:
"""Return the elapsed time in ticks"""
if self._elapsed_time is None:
raise TimerError(f"Timer is not running. Use .start() to start it")
raise TimerError("Timer is not running. Use .start() to start it")

return int(self._elapsed_time * 10000000)


def frequency() -> int:
return 1000000000

Expand Down
25 changes: 15 additions & 10 deletions src/fable-library-py/fable_library/mailbox_processor.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,15 @@

from queue import SimpleQueue
from threading import RLock
from typing import Any, Callable, Generic, List, Optional, TypeVar
from typing import Any, Callable, Generic, Optional, TypeVar

from .async_ import from_continuations, start_immediate
from .async_builder import Async, CancellationToken, OperationCanceledError
from .async_builder import (
Async,
CancellationToken,
Continuations,
OperationCanceledError,
)


_Msg = TypeVar("_Msg")
Expand All @@ -32,7 +37,7 @@ def __init__(
self.body = body

# Holds the continuation i.e the `done` callback of Async.from_continuations returned by `receive`.
self.continuation: List[Callable[[Any], None]] = []
self.continuation: Continuations[Any] | None = None

def post(self, msg: _Msg) -> None:
"""Post a message synchronously to the mailbox processor.
Expand Down Expand Up @@ -67,9 +72,9 @@ def post_and_async_reply(
"""

result: Optional[_Reply] = None
continuation: List[
Callable[[Any], None]
] = None # This is the continuation for the `done` callback of the awaiting poster.
continuation: Continuations[
Any
] | None = None # This is the continuation for the `done` callback of the awaiting poster.

def check_completion() -> None:
if result is not None and continuation is not None:
Expand All @@ -84,7 +89,7 @@ def reply_callback(res: _Reply):
self.messages.put(build_message(reply_channel))
self.__process_events()

def callback(conts: List[Callable[[Any], None]]) -> None:
def callback(conts: Continuations[Any]) -> None:
nonlocal continuation
continuation = conts
check_completion()
Expand All @@ -101,7 +106,7 @@ def receive(self) -> Async[_Msg]:
the timeout is exceeded.
"""

def callback(conts: List[Callable[[Any], None]]):
def callback(conts: Continuations[Any]):
if self.continuation:
raise Exception("Receive can only be called once!")

Expand All @@ -118,7 +123,7 @@ def __process_events(self) -> None:
# Cancellation of async workflows is more tricky in Python than
# with F# so we check the cancellation token for each process.
if self.token.is_cancellation_requested:
self.continuation, cont = [], self.continuation
self.continuation, cont = None, self.continuation
if cont:
cont[2](OperationCanceledError("Mailbox was cancelled"))
return
Expand All @@ -127,7 +132,7 @@ def __process_events(self) -> None:
if self.messages.empty():
return
msg = self.messages.get()
self.continuation, cont = [], self.continuation
self.continuation, cont = None, self.continuation

if cont:
cont[0](msg)
Expand Down
16 changes: 8 additions & 8 deletions src/fable-library-py/fable_library/numeric.py
Original file line number Diff line number Diff line change
@@ -1,42 +1,42 @@
from typing import Optional
from __future__ import annotations

from .types import int64


def to_fixed(x: float, dp: Optional[int] = None) -> str:
def to_fixed(x: float, dp: int | None = None) -> str:
x = int(x) if int(x) == x else x

if dp is not None:
fmt = "{:.%sf}" % dp
return fmt.format(x)

return "{}".format(x)
return f"{x}"


def to_precision(x: float, sd: Optional[int] = None) -> str:
def to_precision(x: float, sd: int | None = None) -> str:
x = int(x) if int(x) == x else x

if sd is not None:
fmt = "{:.%se}" % sd
return fmt.format(x)

return "{}".format(x)
return f"{x}"


def to_exponential(x: float, dp: Optional[int] = None) -> str:
def to_exponential(x: float, dp: int | None = None) -> str:
if dp is not None:
fmt = "{:.%se}" % dp
return fmt.format(x)

return "{}".format(x)
return f"{x}"


def to_hex(x: int) -> str:
def rshift(val: int, n: int) -> int:
sign = 0x10000000000000000 if isinstance(val, int64) else 0x100000000
return val >> n if val >= 0 else (val + sign) >> n

return "{0:x}".format(rshift(x, 0))
return f"{rshift(x, 0):x}"


def multiply(x: float, y: float) -> float:
Expand Down
29 changes: 16 additions & 13 deletions src/fable-library-py/fable_library/observable.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
from __future__ import annotations

from abc import abstractmethod
from typing import Any, Callable, Generic, Optional, Protocol, Tuple, TypeVar
from collections.abc import Callable
from typing import Any, Generic, Protocol, TypeVar

from .choice import (
Choice_tryValueIfChoice1Of2,
Choice_tryValueIfChoice2Of2,
FSharpChoice_2,
Choice_tryValueIfChoice1Of2, # type: ignore
Choice_tryValueIfChoice2Of2, # type: ignore
FSharpChoice_2, # type: ignore
)
from .option import value
from .util import IDisposable
Expand Down Expand Up @@ -42,8 +45,8 @@ class Observer(IObserver[_T]):
def __init__(
self,
on_next: Callable[[_T], None],
on_error: Optional[Callable[[Exception], None]] = None,
on_completed: Optional[Callable[[], None]] = None,
on_error: Callable[[Exception], None] | None = None,
on_completed: Callable[[], None] | None = None,
) -> None:
self._on_next = on_next
self._on_error = on_error or _noop
Expand Down Expand Up @@ -97,11 +100,11 @@ def protect(


def choose(
chooser: Callable[[_T], Optional[_U]], source: IObservable[_T]
chooser: Callable[[_T], _U | None], source: IObservable[_T]
) -> IObservable[_U]:
def subscribe(observer: IObserver[_U]):
def on_next(t: _T) -> None:
def success(u: Optional[_U]) -> None:
def success(u: _U | None) -> None:
if u is not None:
observer.OnNext(value(u))

Expand Down Expand Up @@ -181,9 +184,9 @@ def dispose() -> None:
return Observable(subscribe)


def pairwise(source: IObservable[_T]) -> IObservable[Tuple[_T, _T]]:
def subscribe(observer: IObserver[Tuple[_T, _T]]) -> IDisposable:
last: Optional[_T] = None
def pairwise(source: IObservable[_T]) -> IObservable[tuple[_T, _T]]:
def subscribe(observer: IObserver[tuple[_T, _T]]) -> IDisposable:
last: _T | None = None

def on_next(value: _T) -> None:
nonlocal last
Expand All @@ -200,7 +203,7 @@ def on_next(value: _T) -> None:

def partition(
predicate: Callable[[_T], bool], source: IObservable[_T]
) -> Tuple[IObservable[_T], IObservable[_T]]:
) -> tuple[IObservable[_T], IObservable[_T]]:
return (filter(predicate, source), filter(lambda x: not predicate(x), source))


Expand All @@ -224,7 +227,7 @@ def success(u: _U) -> None:

def split(
splitter: Callable[[_T], FSharpChoice_2], source: IObservable[_T]
) -> Tuple[IObservable[_T], IObservable[_T]]:
) -> tuple[IObservable[_T], IObservable[_T]]:
return (
choose(lambda v: Choice_tryValueIfChoice1Of2(splitter(v)), source),
choose(lambda v: Choice_tryValueIfChoice2Of2(splitter(v)), source),
Expand Down
Loading

0 comments on commit 61437aa

Please sign in to comment.