mirror of
https://github.com/CatalaLang/catala.git
synced 2024-11-08 07:51:43 +03:00
Translation now typechecks
This commit is contained in:
parent
95b34937a6
commit
3f5027e5a5
2
Makefile
2
Makefile
@ -228,6 +228,8 @@ build_french_law_library_js: generate_french_law_library_ocaml format
|
|||||||
#> generate_french_law_library_python : Generates the French law library Python sources from Catala
|
#> generate_french_law_library_python : Generates the French law library Python sources from Catala
|
||||||
generate_french_law_library_python:\
|
generate_french_law_library_python:\
|
||||||
$(FRENCH_LAW_PYTHON_LIB_DIR)/allocations_familiales.py
|
$(FRENCH_LAW_PYTHON_LIB_DIR)/allocations_familiales.py
|
||||||
|
. $(FRENCH_LAW_PYTHON_LIB_DIR)/env/bin/activate ;\
|
||||||
|
$(MAKE) -C $(FRENCH_LAW_PYTHON_LIB_DIR) format
|
||||||
|
|
||||||
#> type_french_law_library_python : Types the French law library Python sources with mypy
|
#> type_french_law_library_python : Types the French law library Python sources with mypy
|
||||||
type_french_law_library_python: generate_french_law_library_python
|
type_french_law_library_python: generate_french_law_library_python
|
||||||
|
@ -6,6 +6,9 @@ dependencies:
|
|||||||
type:
|
type:
|
||||||
mypy $(SOURCES)
|
mypy $(SOURCES)
|
||||||
|
|
||||||
|
format:
|
||||||
|
autopep8 --in-place $(SOURCES)
|
||||||
|
|
||||||
doc:
|
doc:
|
||||||
mkdir -p doc
|
mkdir -p doc
|
||||||
sphinx-build ./ doc
|
sphinx-build ./ doc
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -11,26 +11,247 @@
|
|||||||
|
|
||||||
from gmpy2 import log2, mpz, mpq, mpfr, mpc # type: ignore
|
from gmpy2 import log2, mpz, mpq, mpfr, mpc # type: ignore
|
||||||
import datetime
|
import datetime
|
||||||
import dateutil.relativedelta # type: ignore
|
import dateutil.relativedelta
|
||||||
from typing import NewType, List, Callable, Tuple, Optional, TypeVar, Iterable
|
from typing import NewType, List, Callable, Tuple, Optional, TypeVar, Iterable, Union
|
||||||
from functools import reduce
|
from functools import reduce
|
||||||
|
|
||||||
Alpha = TypeVar('Alpha')
|
Alpha = TypeVar('Alpha')
|
||||||
Beta = TypeVar('Beta')
|
Beta = TypeVar('Beta')
|
||||||
|
|
||||||
# =====
|
# ============
|
||||||
# Types
|
# Type classes
|
||||||
# =====
|
# ============
|
||||||
|
|
||||||
Integer = NewType('Integer', object)
|
|
||||||
Decimal = NewType('Decimal', object)
|
class Integer:
|
||||||
Money = NewType('Money', Integer)
|
def __init__(self, value: Union[str, int]) -> None:
|
||||||
Date = NewType('Date', datetime.date)
|
self.value = mpz(value)
|
||||||
Duration = NewType('Duration', object)
|
|
||||||
|
def __add__(self, other: 'Integer') -> 'Integer':
|
||||||
|
return Integer(self.value + other.value)
|
||||||
|
|
||||||
|
def __sub__(self, other: 'Integer') -> 'Integer':
|
||||||
|
return Integer(self.value - other.value)
|
||||||
|
|
||||||
|
def __mul__(self, other: 'Integer') -> 'Integer':
|
||||||
|
return Integer(self.value * other.value)
|
||||||
|
|
||||||
|
def __truediv__(self, other: 'Integer') -> 'Integer':
|
||||||
|
return Integer(self.value // other.value)
|
||||||
|
|
||||||
|
def __neg__(self: 'Integer') -> 'Integer':
|
||||||
|
return Integer(- self.value)
|
||||||
|
|
||||||
|
def __lt__(self, other: 'Integer') -> bool:
|
||||||
|
return self.value < other.value
|
||||||
|
|
||||||
|
def __le__(self, other: 'Integer') -> bool:
|
||||||
|
return self.value <= other.value
|
||||||
|
|
||||||
|
def __gt__(self, other: 'Integer') -> bool:
|
||||||
|
return self.value > other.value
|
||||||
|
|
||||||
|
def __ge__(self, other: 'Integer') -> bool:
|
||||||
|
return self.value >= other.value
|
||||||
|
|
||||||
|
def __ne__(self, other: object) -> bool:
|
||||||
|
if isinstance(other, Integer):
|
||||||
|
return self.value != other.value
|
||||||
|
else:
|
||||||
|
return True
|
||||||
|
|
||||||
|
def __eq__(self, other: object) -> bool:
|
||||||
|
if isinstance(other, Integer):
|
||||||
|
return self.value == other.value
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
class Decimal:
|
||||||
|
def __init__(self, value: Union[str, int, float, Integer]) -> None:
|
||||||
|
self.value = mpq(value)
|
||||||
|
|
||||||
|
def __add__(self, other: 'Decimal') -> 'Decimal':
|
||||||
|
return Decimal(self.value + other.value)
|
||||||
|
|
||||||
|
def __sub__(self, other: 'Decimal') -> 'Decimal':
|
||||||
|
return Decimal(self.value - other.value)
|
||||||
|
|
||||||
|
def __mul__(self, other: 'Decimal') -> 'Decimal':
|
||||||
|
return Decimal(self.value * other.value)
|
||||||
|
|
||||||
|
def __truediv__(self, other: 'Decimal') -> 'Decimal':
|
||||||
|
return Decimal(self.value / other.value)
|
||||||
|
|
||||||
|
def __neg__(self: 'Decimal') -> 'Decimal':
|
||||||
|
return Decimal(- self.value)
|
||||||
|
|
||||||
|
def __lt__(self, other: 'Decimal') -> bool:
|
||||||
|
return self.value < other.value
|
||||||
|
|
||||||
|
def __le__(self, other: 'Decimal') -> bool:
|
||||||
|
return self.value <= other.value
|
||||||
|
|
||||||
|
def __gt__(self, other: 'Decimal') -> bool:
|
||||||
|
return self.value > other.value
|
||||||
|
|
||||||
|
def __ge__(self, other: 'Decimal') -> bool:
|
||||||
|
return self.value >= other.value
|
||||||
|
|
||||||
|
def __ne__(self, other: object) -> bool:
|
||||||
|
if isinstance(other, Decimal):
|
||||||
|
return self.value != other.value
|
||||||
|
else:
|
||||||
|
return True
|
||||||
|
|
||||||
|
def __eq__(self, other: object) -> bool:
|
||||||
|
if isinstance(other, Decimal):
|
||||||
|
return self.value == other.value
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
class Money:
|
||||||
|
def __init__(self, value: Integer) -> None:
|
||||||
|
self.value = value
|
||||||
|
|
||||||
|
def __add__(self, other: 'Money') -> 'Money':
|
||||||
|
return Money(self.value + other.value)
|
||||||
|
|
||||||
|
def __sub__(self, other: 'Money') -> 'Money':
|
||||||
|
return Money(self.value - other.value)
|
||||||
|
|
||||||
|
def __mul__(self, other: Decimal) -> 'Money':
|
||||||
|
return Money(Integer(self.value.value * other.value))
|
||||||
|
|
||||||
|
def __truediv__(self, other: 'Money') -> Decimal:
|
||||||
|
return Decimal(mpq(self.value.value / other.value.value))
|
||||||
|
|
||||||
|
def __neg__(self: 'Money') -> 'Money':
|
||||||
|
return Money(- self.value)
|
||||||
|
|
||||||
|
def __lt__(self, other: 'Money') -> bool:
|
||||||
|
return self.value < other.value
|
||||||
|
|
||||||
|
def __le__(self, other: 'Money') -> bool:
|
||||||
|
return self.value <= other.value
|
||||||
|
|
||||||
|
def __gt__(self, other: 'Money') -> bool:
|
||||||
|
return self.value > other.value
|
||||||
|
|
||||||
|
def __ge__(self, other: 'Money') -> bool:
|
||||||
|
return self.value >= other.value
|
||||||
|
|
||||||
|
def __ne__(self, other: object) -> bool:
|
||||||
|
if isinstance(other, Money):
|
||||||
|
return self.value != other.value
|
||||||
|
else:
|
||||||
|
return True
|
||||||
|
|
||||||
|
def __eq__(self, other: object) -> bool:
|
||||||
|
if isinstance(other, Money):
|
||||||
|
return self.value == other.value
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
class Date:
|
||||||
|
def __init__(self, value: datetime.date) -> None:
|
||||||
|
self.value = value
|
||||||
|
|
||||||
|
def __add__(self, other: 'Duration') -> 'Date':
|
||||||
|
return Date(self.value + other.value)
|
||||||
|
|
||||||
|
def __sub__(self, other: 'Date') -> 'Duration':
|
||||||
|
# Careful: invert argument order
|
||||||
|
return Duration(dateutil.relativedelta.relativedelta(other.value, self.value))
|
||||||
|
|
||||||
|
def __lt__(self, other: 'Date') -> bool:
|
||||||
|
return self.value < other.value
|
||||||
|
|
||||||
|
def __le__(self, other: 'Date') -> bool:
|
||||||
|
return self.value <= other.value
|
||||||
|
|
||||||
|
def __gt__(self, other: 'Date') -> bool:
|
||||||
|
return self.value > other.value
|
||||||
|
|
||||||
|
def __ge__(self, other: 'Date') -> bool:
|
||||||
|
return self.value >= other.value
|
||||||
|
|
||||||
|
def __ne__(self, other: object) -> bool:
|
||||||
|
if isinstance(other, Date):
|
||||||
|
return self.value != other.value
|
||||||
|
else:
|
||||||
|
return True
|
||||||
|
|
||||||
|
def __eq__(self, other: object) -> bool:
|
||||||
|
if isinstance(other, Date):
|
||||||
|
return self.value == other.value
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
class Duration:
|
||||||
|
def __init__(self, value: dateutil.relativedelta.relativedelta) -> None:
|
||||||
|
self.value = value
|
||||||
|
|
||||||
|
def __add__(self, other: 'Duration') -> 'Duration':
|
||||||
|
return Duration(self.value + other.value)
|
||||||
|
|
||||||
|
def __sub__(self, other: 'Duration') -> 'Duration':
|
||||||
|
return Duration(self.value - other.value)
|
||||||
|
|
||||||
|
def __neg__(self: 'Duration') -> 'Duration':
|
||||||
|
return Duration(- self.value)
|
||||||
|
|
||||||
|
def __lt__(self, other: 'Duration') -> bool:
|
||||||
|
x = self.value.normalized()
|
||||||
|
y = other.value.normalized()
|
||||||
|
if (x.years != 0 or y.years != 0 or x.months != 0 or y.months != 0):
|
||||||
|
raise Exception("Can only compare durations expressed in days")
|
||||||
|
else:
|
||||||
|
return x.days < y.days
|
||||||
|
|
||||||
|
def __le__(self, other: 'Duration') -> bool:
|
||||||
|
x = self.value.normalized()
|
||||||
|
y = other.value.normalized()
|
||||||
|
if (x.years != 0 or y.years != 0 or x.months != 0 or y.months != 0):
|
||||||
|
raise Exception("Can only compare durations expressed in days")
|
||||||
|
else:
|
||||||
|
return x.days <= y.days
|
||||||
|
|
||||||
|
def __gt__(self, other: 'Duration') -> bool:
|
||||||
|
x = self.value.normalized()
|
||||||
|
y = other.value.normalized()
|
||||||
|
if (x.years != 0 or y.years != 0 or x.months != 0 or y.months != 0):
|
||||||
|
raise Exception("Can only compare durations expressed in days")
|
||||||
|
else:
|
||||||
|
return x.days > y.days
|
||||||
|
|
||||||
|
def __ge__(self, other: 'Duration') -> bool:
|
||||||
|
x = self.value.normalized()
|
||||||
|
y = other.value.normalized()
|
||||||
|
if (x.years != 0 or y.years != 0 or x.months != 0 or y.months != 0):
|
||||||
|
raise Exception("Can only compare durations expressed in days")
|
||||||
|
else:
|
||||||
|
return x.days >= y.days
|
||||||
|
|
||||||
|
def __ne__(self, other: object) -> bool:
|
||||||
|
if isinstance(other, Duration):
|
||||||
|
return self.value != other.value
|
||||||
|
else:
|
||||||
|
return True
|
||||||
|
|
||||||
|
def __eq__(self, other: object) -> bool:
|
||||||
|
if isinstance(other, Duration):
|
||||||
|
return self.value == other.value
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
class Unit:
|
class Unit:
|
||||||
pass
|
def __init__(self) -> None:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
class SourcePosition:
|
class SourcePosition:
|
||||||
@ -69,58 +290,6 @@ class NoValueProvided(Exception):
|
|||||||
def __init__(self, source_position: SourcePosition) -> None:
|
def __init__(self, source_position: SourcePosition) -> None:
|
||||||
self.source_position = SourcePosition
|
self.source_position = SourcePosition
|
||||||
|
|
||||||
|
|
||||||
def raise_(ex):
|
|
||||||
raise ex
|
|
||||||
|
|
||||||
|
|
||||||
class TryCatch:
|
|
||||||
def __init__(self, fun, *args, **kwargs):
|
|
||||||
self.fun = fun
|
|
||||||
self.args = args
|
|
||||||
self.kwargs = kwargs
|
|
||||||
|
|
||||||
self.exception_types_and_handlers = []
|
|
||||||
self.finalize = None
|
|
||||||
|
|
||||||
def rescue(self, exception_types, handler):
|
|
||||||
if not isinstance(exception_types, Iterable):
|
|
||||||
exception_types = (exception_types,)
|
|
||||||
|
|
||||||
self.exception_types_and_handlers.append((exception_types, handler))
|
|
||||||
return self
|
|
||||||
|
|
||||||
def ensure(self, finalize, *finalize_args, **finalize_kwargs):
|
|
||||||
if self.finalize is not None:
|
|
||||||
raise Exception('ensure() called twice')
|
|
||||||
|
|
||||||
self.finalize = finalize
|
|
||||||
self.finalize_args = finalize_args
|
|
||||||
self.finalize_kwargs = finalize_kwargs
|
|
||||||
return self
|
|
||||||
|
|
||||||
def __call__(self):
|
|
||||||
try:
|
|
||||||
return self.fun(*self.args, **self.kwargs)
|
|
||||||
|
|
||||||
except BaseException as exc:
|
|
||||||
handler = self.find_applicable_handler(exc)
|
|
||||||
if handler is None:
|
|
||||||
raise
|
|
||||||
return handler(exc)
|
|
||||||
|
|
||||||
finally:
|
|
||||||
if self.finalize is not None:
|
|
||||||
self.finalize()
|
|
||||||
|
|
||||||
def find_applicable_handler(self, exc):
|
|
||||||
applicable_handlers = (
|
|
||||||
handler
|
|
||||||
for exception_types, handler in self.exception_types_and_handlers
|
|
||||||
if isinstance(exc, exception_types)
|
|
||||||
)
|
|
||||||
return next(applicable_handlers, None)
|
|
||||||
|
|
||||||
# ============================
|
# ============================
|
||||||
# Constructors and conversions
|
# Constructors and conversions
|
||||||
# ============================
|
# ============================
|
||||||
@ -131,19 +300,19 @@ class TryCatch:
|
|||||||
|
|
||||||
|
|
||||||
def money_of_cents_string(v: str) -> Money:
|
def money_of_cents_string(v: str) -> Money:
|
||||||
return Money(mpz(v))
|
return Money(Integer(v))
|
||||||
|
|
||||||
|
|
||||||
def money_of_cents_int(v: int) -> Money:
|
def money_of_cents_int(v: int) -> Money:
|
||||||
return Money(mpz(v))
|
return Money(Integer(v))
|
||||||
|
|
||||||
|
|
||||||
def money_of_cents_integer(v: Integer) -> Money:
|
def money_of_cents_integer(v: Integer) -> Money:
|
||||||
return Money(mpz(v))
|
return Money(v)
|
||||||
|
|
||||||
|
|
||||||
def money_to_float(m: Money) -> float:
|
def money_to_float(m: Money) -> float:
|
||||||
return float(mpfr(mpq(m, 100)))
|
return float(mpfr(mpq(m.value.value, 100)))
|
||||||
|
|
||||||
|
|
||||||
def money_to_string(m: Money) -> str:
|
def money_to_string(m: Money) -> str:
|
||||||
@ -151,7 +320,7 @@ def money_to_string(m: Money) -> str:
|
|||||||
|
|
||||||
|
|
||||||
def money_to_cents(m: Money) -> Integer:
|
def money_to_cents(m: Money) -> Integer:
|
||||||
return m
|
return m.value
|
||||||
|
|
||||||
# --------
|
# --------
|
||||||
# Decimals
|
# Decimals
|
||||||
@ -159,23 +328,23 @@ def money_to_cents(m: Money) -> Integer:
|
|||||||
|
|
||||||
|
|
||||||
def decimal_of_string(d: str) -> Decimal:
|
def decimal_of_string(d: str) -> Decimal:
|
||||||
return Decimal(mpq(d))
|
return Decimal(d)
|
||||||
|
|
||||||
|
|
||||||
def decimal_to_float(d: Decimal) -> float:
|
def decimal_to_float(d: Decimal) -> float:
|
||||||
return float(mpfr(d))
|
return float(mpfr(d.value))
|
||||||
|
|
||||||
|
|
||||||
def decimal_of_float(d: float) -> Decimal:
|
def decimal_of_float(d: float) -> Decimal:
|
||||||
return Decimal(mpq(d))
|
return Decimal(d)
|
||||||
|
|
||||||
|
|
||||||
def decimal_of_integer(d: Integer) -> Decimal:
|
def decimal_of_integer(d: Integer) -> Decimal:
|
||||||
return Decimal(mpq(d))
|
return Decimal(d.value)
|
||||||
|
|
||||||
|
|
||||||
def decimal_to_string(precision: int, i: Decimal) -> str:
|
def decimal_to_string(precision: int, i: Decimal) -> str:
|
||||||
return "{1:.{0}}".format(precision, mpfr(i, precision * 10 // 2))
|
return "{1:.{0}}".format(precision, mpfr(i.value, precision * 10 // 2))
|
||||||
|
|
||||||
# --------
|
# --------
|
||||||
# Integers
|
# Integers
|
||||||
@ -183,19 +352,19 @@ def decimal_to_string(precision: int, i: Decimal) -> str:
|
|||||||
|
|
||||||
|
|
||||||
def integer_of_string(s: str) -> Integer:
|
def integer_of_string(s: str) -> Integer:
|
||||||
return Integer(mpz(s))
|
return Integer(s)
|
||||||
|
|
||||||
|
|
||||||
def integer_to_string(d: Integer) -> str:
|
def integer_to_string(d: Integer) -> str:
|
||||||
return str(d)
|
return str(d.value)
|
||||||
|
|
||||||
|
|
||||||
def integer_of_int(d: int) -> Integer:
|
def integer_of_int(d: int) -> Integer:
|
||||||
return Integer(mpz(d))
|
return Integer(d)
|
||||||
|
|
||||||
|
|
||||||
def integer_to_int(d: Integer) -> int:
|
def integer_to_int(d: Integer) -> int:
|
||||||
return int(d) # type: ignore
|
return int(d.value)
|
||||||
|
|
||||||
|
|
||||||
def integer_exponentiation(i: Integer, e: int) -> Integer:
|
def integer_exponentiation(i: Integer, e: int) -> Integer:
|
||||||
@ -203,7 +372,7 @@ def integer_exponentiation(i: Integer, e: int) -> Integer:
|
|||||||
|
|
||||||
|
|
||||||
def integer_log2(i: Integer) -> int:
|
def integer_log2(i: Integer) -> int:
|
||||||
return int(log2(i))
|
return int(log2(i.value))
|
||||||
|
|
||||||
# -----
|
# -----
|
||||||
# Dates
|
# Dates
|
||||||
@ -211,19 +380,19 @@ def integer_log2(i: Integer) -> int:
|
|||||||
|
|
||||||
|
|
||||||
def day_of_month_of_date(d: Date) -> Integer:
|
def day_of_month_of_date(d: Date) -> Integer:
|
||||||
return integer_of_int(d.day)
|
return integer_of_int(d.value.day)
|
||||||
|
|
||||||
|
|
||||||
def month_number_of_date(d: Date) -> Integer:
|
def month_number_of_date(d: Date) -> Integer:
|
||||||
return integer_of_int(d.month)
|
return integer_of_int(d.value.month)
|
||||||
|
|
||||||
|
|
||||||
def year_of_date(d: Date) -> Integer:
|
def year_of_date(d: Date) -> Integer:
|
||||||
return integer_of_int(d.year)
|
return integer_of_int(d.value.year)
|
||||||
|
|
||||||
|
|
||||||
def date_to_string(d: Date) -> str:
|
def date_to_string(d: Date) -> str:
|
||||||
return "{}".format(d)
|
return "{}".format(d.value)
|
||||||
|
|
||||||
|
|
||||||
def date_of_numbers(year: int, month: int, day: int) -> Date:
|
def date_of_numbers(year: int, month: int, day: int) -> Date:
|
||||||
@ -239,11 +408,11 @@ def duration_of_numbers(years: int, months: int, days: int) -> Duration:
|
|||||||
|
|
||||||
|
|
||||||
def duration_to_years_months_days(d: Duration) -> Tuple[int, int, int]:
|
def duration_to_years_months_days(d: Duration) -> Tuple[int, int, int]:
|
||||||
return (d.years, d.months, d.days) # type: ignore
|
return (d.value.years, d.value.months, d.value.days) # type: ignore
|
||||||
|
|
||||||
|
|
||||||
def duration_to_string(s: Duration) -> str:
|
def duration_to_string(s: Duration) -> str:
|
||||||
return "{}".format(s)
|
return "{}".format(s.value)
|
||||||
|
|
||||||
# -----
|
# -----
|
||||||
# Lists
|
# Lists
|
||||||
@ -297,9 +466,10 @@ def handle_default(
|
|||||||
return acc
|
return acc
|
||||||
|
|
||||||
|
|
||||||
def no_input() -> Callable[[], Alpha]:
|
def no_input() -> Callable[[Unit], Alpha]:
|
||||||
# From https://stackoverflow.com/questions/8294618/define-a-lambda-expression-that-raises-an-exception
|
def closure(_: Unit()):
|
||||||
return (_ for _ in ()).throw(EmptyError)
|
raise EmptyError
|
||||||
|
return closure
|
||||||
|
|
||||||
# =======
|
# =======
|
||||||
# Logging
|
# Logging
|
||||||
|
@ -2,4 +2,6 @@ gmpy2
|
|||||||
typing
|
typing
|
||||||
mypy
|
mypy
|
||||||
python-dateutil
|
python-dateutil
|
||||||
|
types-python-dateutil
|
||||||
sphinx
|
sphinx
|
||||||
|
autopep8
|
Loading…
Reference in New Issue
Block a user