Skip to content

Commit

Permalink
Check for duplicated time-dependent functions.
Browse files Browse the repository at this point in the history
  • Loading branch information
MImmesberger committed Jan 25, 2025
1 parent da4aa6a commit 00de1e9
Show file tree
Hide file tree
Showing 2 changed files with 23 additions and 55 deletions.
23 changes: 23 additions & 0 deletions src/_gettsim/functions/loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,9 +115,32 @@ def get_active_functions_from_module(
)
]

_fail_if_multiple_active_functions_with_same_qualified_name(functions_in_module)

return {func.leaf_name: func for func in functions_in_module}


def _fail_if_multiple_active_functions_with_same_qualified_name(
functions: list[PolicyFunction],
) -> None:
qualified_names = [func.qualified_name for func in functions]
duplicated_func_specs = (
(func.original_function_name, func.start_date, func.end_date)
for func in functions
if qualified_names.count(func.qualified_name) > 1
)
if duplicated_func_specs:
functions_and_dates = [
f"{func_name} (start_date: {start_date}), end_date: {end_date})\n"
for func_name, start_date, end_date in duplicated_func_specs
]
msg = """
Some functions are active at the same time and have the same qualified name.
This is likely due to overlapping start and end dates. The following functions
are affected: \n\n""" + "; ".join(functions_and_dates)
raise ValueError(msg)


def _find_python_files_recursively(roots: list[Path]) -> list[Path]:
"""
Find all Python files reachable from the given roots.
Expand Down
55 changes: 0 additions & 55 deletions src/_gettsim/shared.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import operator
import textwrap
from collections.abc import Callable
from datetime import date
from typing import Any, TypeVar

import numpy
Expand All @@ -24,60 +23,6 @@ def __repr__(self):
return str(self)


TIME_DEPENDENT_FUNCTIONS: dict[str, list[Callable]] = {}


def _check_for_conflicts_in_time_dependent_functions(
dag_key: str, function_name: str, start: date, end: date
):
"""
Raises an error if a different time-dependent function has already been registered
for the given dag_key and their date ranges overlap.
"""

if dag_key not in TIME_DEPENDENT_FUNCTIONS:
return

for f in TIME_DEPENDENT_FUNCTIONS[dag_key]:
# A function is not conflicting with itself. We compare names instead of
# identities since functions might get wrapped, which would change their
# identity but not their name.
if f.__name__ != function_name and (
start <= f.__info__["start_date"] <= end
or f.__info__["start_date"] <= start <= f.__info__["end_date"]
):
raise ConflictingTimeDependentFunctionsError(
dag_key,
function_name,
start,
end,
f.__name__,
f.__info__["start_date"],
f.__info__["end_date"],
)


class ConflictingTimeDependentFunctionsError(Exception):
"""Raised when two time-dependent functions have overlapping time ranges."""

def __init__( # noqa: PLR0913
self,
dag_key: str,
function_name_1: str,
start_1: date,
end_1: date,
function_name_2: str,
start_2: date,
end_2: date,
):
super().__init__(
f"Conflicting functions for key {dag_key!r}: "
f"{function_name_1!r} ({start_1} to {end_1}) vs. "
f"{function_name_2!r} ({start_2} to {end_2}).\n\n"
f"Overlapping from {max(start_1, start_2)} to {min(end_1, end_2)}."
)


def format_list_linewise(list_):
formatted_list = '",\n "'.join(list_)
return textwrap.dedent(
Expand Down

0 comments on commit 00de1e9

Please sign in to comment.