Skip to content

Commit

Permalink
Merge branch 'hjafari/MIC-5600_interface_typing' of github.com:ihmeuw…
Browse files Browse the repository at this point in the history
…/vivarium into hjafari/MIC-5600_interface_typing
  • Loading branch information
hussain-jafari committed Dec 11, 2024
2 parents f331eed + a85ca3a commit c77bc8f
Show file tree
Hide file tree
Showing 7 changed files with 55 additions and 49 deletions.
1 change: 1 addition & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ jobs:
ihmeuw/vivarium_build_utils/.github/workflows/build.yml@main
with:
dependencies: "layered_config_tree"
python_version: ${{ matrix.python-version }}
secrets:
notify_email: ${{ secrets.NOTIFY_EMAIL }}
NOTIFY_PASSWORD: ${{ secrets.NOTIFY_PASSWORD }}
Expand Down
4 changes: 3 additions & 1 deletion CHANGELOG.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
**3.2.5 - 12/09/24**
**3.2.5 - 12/11/24**

- Type-hinting: Fix mypy errors in vivarium/framework/results/interface.py
- Type-hinting: Fix mypy errors in vivarium/component.py
- Type-hinting: Fix mypy errors in vivarium/framework/results/observer.py

**3.2.4 - 12/03/24**

Expand Down
2 changes: 0 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ exclude = [
# You will need to remove the mypy: ignore-errors comment from the file heading as well
'docs/source/conf.py',
'setup.py',
'src/vivarium/component.py',
'src/vivarium/examples/boids/forces.py',
'src/vivarium/examples/boids/movement.py',
'src/vivarium/examples/boids/neighbors.py',
Expand All @@ -50,7 +49,6 @@ exclude = [
'src/vivarium/framework/engine.py',
'src/vivarium/framework/population/manager.py',
'src/vivarium/framework/population/population_view.py',
'src/vivarium/framework/results/observer.py',
'src/vivarium/framework/state_machine.py',
'src/vivarium/interface/cli.py',
'src/vivarium/interface/interactive.py',
Expand Down
89 changes: 48 additions & 41 deletions src/vivarium/component.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
# mypy: ignore-errors
"""
=========
Component
Expand All @@ -18,16 +17,17 @@
from importlib import import_module
from inspect import signature
from numbers import Number
from typing import TYPE_CHECKING, Any
from typing import TYPE_CHECKING, Any, cast

import pandas as pd
from layered_config_tree import ConfigurationError, LayeredConfigTree
from loguru._logger import Logger

from vivarium.framework.artifact import ArtifactException
from vivarium.framework.population import PopulationError

if TYPE_CHECKING:
import loguru

from vivarium.framework.engine import Builder
from vivarium.framework.event import Event
from vivarium.framework.lookup import LookupTable
Expand Down Expand Up @@ -92,7 +92,24 @@ class Component(ABC):
component. An empty dictionary indicates no managed configurations.
"""

def __repr__(self):
def __init__(self) -> None:
"""Initializes a new instance of the Component class.
This method is the initializer for the Component class. It initializes
logger of type Logger and population_view of type PopulationView to None.
These attributes will be fully initialized in the setup_component method
of this class.
"""
self._repr: str = ""
self._name: str = ""
self._sub_components: list["Component"] = []
self.logger: loguru.Logger | None = None
self.get_value_columns: Callable[[str | pd.DataFrame], list[str]] | None = None
self.configuration: LayeredConfigTree | None = None
self._population_view: PopulationView | None = None
self.lookup_tables: dict[str, LookupTable] = {}

def __repr__(self) -> str:
"""Returns a string representation of the __init__ call made to create this
object.
Expand All @@ -111,16 +128,17 @@ def __repr__(self):
object.
"""
if not self._repr:
args = [
f"{name}={value.__repr__() if isinstance(value, Component) else value}"
for name, value in self.get_initialization_parameters().items()
]
args = ", ".join(args)
args = ", ".join(
[
f"{name}={value.__repr__() if isinstance(value, Component) else value}"
for name, value in self.get_initialization_parameters().items()
]
)
self._repr = f"{type(self).__name__}({args})"

return self._repr

def __str__(self):
def __str__(self) -> str:
return self._repr

##############
Expand Down Expand Up @@ -244,15 +262,15 @@ def initialization_requirements(
return []

@property
def population_view_query(self) -> str | None:
def population_view_query(self) -> str:
"""Provides a query to use when filtering the component's `PopulationView`.
Returns
-------
A pandas query string for filtering the component's `PopulationView`.
Returns `None` if no filtering is required.
Returns an empty string if no filtering is required.
"""
return None
return ""

@property
def post_setup_priority(self) -> int:
Expand Down Expand Up @@ -324,23 +342,6 @@ def simulation_end_priority(self) -> int:
# Lifecycle methods #
#####################

def __init__(self) -> None:
"""Initializes a new instance of the Component class.
This method is the initializer for the Component class. It initializes
logger of type Logger and population_view of type PopulationView to None.
These attributes will be fully initialized in the setup_component method
of this class.
"""
self._repr: str = ""
self._name: str = ""
self._sub_components: list["Component"] = []
self.logger: Logger | None = None
self.get_value_columns: Callable[[str | pd.DataFrame], list[str]] | None = None
self.configuration: LayeredConfigTree | None = None
self._population_view: PopulationView | None = None
self.lookup_tables: dict[str, LookupTable] = {}

def setup_component(self, builder: "Builder") -> None:
"""Sets up the component for a Vivarium simulation.
Expand Down Expand Up @@ -515,7 +516,7 @@ def get_initialization_parameters(self) -> dict[str, Any]:
"""
return {
parameter_name: getattr(self, parameter_name)
for parameter_name in signature(self.__init__).parameters
for parameter_name in signature(self.__init__).parameters # type: ignore[misc]
if hasattr(self, parameter_name)
}

Expand All @@ -538,7 +539,7 @@ def get_configuration(self, builder: "Builder") -> LayeredConfigTree | None:
"""

if self.name in builder.configuration:
return builder.configuration[self.name]
return builder.configuration.get_tree(self.name)
return None

def build_all_lookup_tables(self, builder: "Builder") -> None:
Expand Down Expand Up @@ -606,7 +607,9 @@ def build_lookup_table(
raise ConfigurationError(f"Data '{data}' must be a LookupTableData instance.")

if isinstance(data, list):
return builder.lookup.build_table(data, value_columns=list(value_columns))
return builder.lookup.build_table(
data, value_columns=list(value_columns) if value_columns else ()
)
if isinstance(data, pd.DataFrame):
duplicated_columns = set(data.columns[data.columns.duplicated()])
if duplicated_columns:
Expand All @@ -627,11 +630,15 @@ def build_lookup_table(
return builder.lookup.build_table(data)

def _get_columns(
self, value_columns: Sequence[str] | None, data: float | pd.DataFrame
) -> tuple[list[str], list[str], list[str]]:
self, value_columns: Sequence[str] | None, data: pd.DataFrame
) -> tuple[Sequence[str], list[str], list[str]]:
all_columns = list(data.columns)
if value_columns is None:
value_columns = self.get_value_columns(data)
# NOTE: self.get_value_columns cannot be None at this point of the call stack
value_column_getter = cast(
Callable[[str | pd.DataFrame], list[str]], self.get_value_columns
)
value_columns = value_column_getter(data)

potential_parameter_columns = [
str(col).removesuffix("_start")
Expand Down Expand Up @@ -685,9 +692,9 @@ def get_data(self, builder: Builder, data_source: DataInput) -> Any:
module, method = data_source.split("::")
try:
if module == "self":
data_source = getattr(self, method)
data_source_callable = getattr(self, method)
else:
data_source = getattr(import_module(module), method)
data_source_callable = getattr(import_module(module), method)
except ModuleNotFoundError:
raise ConfigurationError(f"Unable to find module '{module}'.")
except AttributeError:
Expand All @@ -697,15 +704,15 @@ def get_data(self, builder: Builder, data_source: DataInput) -> Any:
raise ConfigurationError(
f"There is no method '{method}' for the {module_string}."
)
data = data_source(builder)
data = data_source_callable(builder)
else:
try:
data = builder.data.load(data_source)
except ArtifactException:
raise ConfigurationError(
f"Failed to find key '{data_source}' in artifact."
)
elif isinstance(data_source, Callable):
elif callable(data_source):
data = data_source(builder)
else:
data = data_source
Expand Down Expand Up @@ -791,7 +798,7 @@ def _register_simulant_initializer(self, builder: Builder) -> None:

if type(self).on_initialize_simulants != Component.on_initialize_simulants:
builder.population.initializes_simulants(
self, creates_columns=self.columns_created, **initialization_requirements
self, creates_columns=self.columns_created, **initialization_requirements # type: ignore[arg-type]
)

def _register_time_step_prepare_listener(self, builder: "Builder") -> None:
Expand Down
5 changes: 2 additions & 3 deletions src/vivarium/framework/logging/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
from __future__ import annotations

import loguru
from loguru import logger

from vivarium.framework.logging.utilities import configure_logging_to_terminal
from vivarium.manager import Interface, Manager
Expand Down Expand Up @@ -38,7 +37,7 @@ def _terminal_logging_not_configured() -> bool:
# fragile since it depends on a loguru's internals as well as the stability of code
# paths in vivarium, but both are quite stable at this point, so I think it's pretty,
# low risk.
return 1 not in logger._core.handlers # type: ignore[attr-defined]
return 1 not in loguru.logger._core.handlers # type: ignore[attr-defined]

@property
def name(self) -> str:
Expand All @@ -48,7 +47,7 @@ def get_logger(self, component_name: str | None = None) -> loguru.Logger:
bind_args = {"simulation": self._simulation_name}
if component_name:
bind_args["component"] = component_name
return logger.bind(**bind_args)
return loguru.logger.bind(**bind_args)


class LoggingInterface(Interface):
Expand Down
1 change: 0 additions & 1 deletion src/vivarium/framework/results/observer.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
# mypy: ignore-errors
"""
=========
Observers
Expand Down
2 changes: 1 addition & 1 deletion tests/framework/test_engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ def components():

@pytest.fixture
def log(mocker):
return mocker.patch("vivarium.framework.logging.manager.logger")
return mocker.patch("vivarium.framework.logging.manager.loguru.logger")


def test_simulation_with_non_components(SimulationContext, components: list[Component]):
Expand Down

0 comments on commit c77bc8f

Please sign in to comment.