Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed Typehint for RunSettings.colocated_db_settings #462

Merged
merged 19 commits into from
Jan 29, 2024
Merged
2 changes: 1 addition & 1 deletion smartsim/_core/_cli/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ def check_py_torch_version(versions: Versioner, device_in: _TDeviceStr = "cpu")
"Torch version not found in python environment. "
"Attempting to install via `pip`"
)
wheel_device = device if device == "cpu" else device_suffix.replace("+","")
wheel_device = device if device == "cpu" else device_suffix.replace("+", "")
pip(
"install",
"--extra-index-url",
Expand Down
2 changes: 1 addition & 1 deletion smartsim/_core/control/controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -631,7 +631,7 @@ def _prep_entity_client_env(self, entity: Model) -> None:
# Set address to local if it's a colocated model
if entity.colocated and entity.run_settings.colocated_db_settings is not None:
db_name_colo = entity.run_settings.colocated_db_settings["db_identifier"]

assert isinstance(db_name_colo, str)
for key in address_dict:
_, db_id = unpack_db_identifier(key, "_")
if db_name_colo == db_id:
Expand Down
2 changes: 1 addition & 1 deletion smartsim/_core/launcher/step/step.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@
)
makedirs(osp.dirname(script_path), exist_ok=True)

db_settings: t.Dict[str, str] = {}
db_settings = {}

Check warning on line 96 in smartsim/_core/launcher/step/step.py

View check run for this annotation

Codecov / codecov/patch

smartsim/_core/launcher/step/step.py#L96

Added line #L96 was not covered by tests
if isinstance(self.step_settings, RunSettings):
db_settings = self.step_settings.colocated_db_settings or {}

Expand Down
56 changes: 43 additions & 13 deletions smartsim/entity/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -258,11 +258,12 @@ def colocate_db_uds(
f"Invalid name for unix socket: {unix_socket}. Must only "
"contain alphanumeric characters or . : _ - /"
)

uds_options = {
assert isinstance(unix_socket, str)
assert isinstance(socket_permissions, int)
uds_options: t.Dict[str, t.Union[int, str]] = {
"unix_socket": unix_socket,
"socket_permissions": socket_permissions,
"port": 0, # This is hardcoded to 0 as recommended by redis for UDS
"port": 0, # 0 as recommended by redis for UDS
}

common_options = {
Expand Down Expand Up @@ -328,13 +329,23 @@ def colocate_db_tcp(
"debug": debug,
"db_identifier": db_identifier,
}
# test
self._set_colocated_db_settings(tcp_options, common_options, **kwargs)

def _set_colocated_db_settings(
self,
connection_options: t.Dict[str, t.Any],
common_options: t.Dict[str, t.Any],
**kwargs: t.Any,
connection_options: t.Mapping[str, t.Union[int, list[str], str]],
common_options: t.Dict[
str,
t.Union[
t.Union[t.Iterable[t.Union[int, t.Iterable[int]]], None],
bool,
int,
str,
None,
],
],
**kwargs: t.Union[int, None],
) -> None:
"""
Ingest the connection-specific options (UDS/TCP) and set the final settings
Expand All @@ -357,21 +368,40 @@ def _set_colocated_db_settings(
)

# TODO list which db settings can be extras
common_options["custom_pinning"] = self._create_pinning_string(
common_options["custom_pinning"], common_options["cpus"]
x = t.cast(
t.Optional[t.Iterable[t.Union[int, t.Iterable[int]]]],
common_options.get("custom_pinning"),
)

colo_db_config = {}
e = t.cast(int, common_options.get("cpus"))
common_options["custom_pinning"] = self._create_pinning_string(x, e)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we change the names of these of these intermediary variables to something a bit more descriptive (maybe custom_pinning_ and cpus_)?

Or maybe just remove them entirely?

common_options["custom_pinning"] = self._create_pinning_string(
    t.cast(
        t.Optional[t.Iterable[t.Union[int, t.Iterable[int]]]], 
        common_options["custom_pinning"], common_options["cpus"]
    ),
    t.cast(int, common_options.get("cpus"))
)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would almost suggest keeping the vars for readability, but Im not as experienced so let me know!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Totally willing to keep the intermediate variables! I think I agree that they improve readability!


colo_db_config: t.Dict[
str,
t.Union[
bool,
int,
str,
None,
list[str],
t.Iterable[t.Union[int, t.Iterable[int]]],
list[DBModel],
list[DBScript],
t.Dict[str, t.Union[int, None]],
t.Dict[str, str],
],
] = {}
colo_db_config.update(connection_options)
colo_db_config.update(common_options)
# redisai arguments for inference settings
colo_db_config["rai_args"] = {

redis_ai_temp = {
"threads_per_queue": kwargs.get("threads_per_queue", None),
"inter_op_parallelism": kwargs.get("inter_op_parallelism", None),
"intra_op_parallelism": kwargs.get("intra_op_parallelism", None),
}
# redisai arguments for inference settings
colo_db_config["rai_args"] = redis_ai_temp
colo_db_config["extra_db_args"] = {
k: str(v) for k, v in kwargs.items() if k not in colo_db_config["rai_args"]
k: str(v) for k, v in kwargs.items() if k not in redis_ai_temp
}

self._check_db_objects_colo()
Expand Down
19 changes: 18 additions & 1 deletion smartsim/settings/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
from smartsim.settings.containers import Container

from .._core.utils.helpers import expand_exe_path, fmt_dict, is_valid_cmd
from ..entity.dbobject import DBModel, DBScript
from ..log import get_logger

logger = get_logger(__name__)
Expand Down Expand Up @@ -96,7 +97,23 @@ def __init__(
self.container = container
self._run_command = run_command
self.in_batch = False
self.colocated_db_settings: t.Optional[t.Dict[str, str]] = None
self.colocated_db_settings: t.Optional[
t.Dict[
str,
t.Union[
bool,
int,
str,
None,
list[str],
t.Iterable[t.Union[int, t.Iterable[int]]],
list[DBModel],
list[DBScript],
t.Dict[str, t.Union[int, None]],
t.Dict[str, str],
],
]
] = None

@property
def exe_args(self) -> t.Union[str, t.List[str]]:
Expand Down
3 changes: 2 additions & 1 deletion smartsim/settings/lsfSettings.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,8 @@ def set_cpus_per_rs(self, cpus_per_rs: int) -> None:
:type cpus_per_rs: int or str
"""
if self.colocated_db_settings:
db_cpus = int(self.colocated_db_settings.get("db_cpus", 0))
db_cpus = self.colocated_db_settings.get("db_cpus", 0)
assert isinstance(db_cpus, int)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor refactor request here to make sure this is not a breaking API change

db_cpus = int(t.cast(int, self.colocated_db_settings.get("db_cpus", 0)))
# and then you should be free to remove the `assert stmt` as well!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If Im already casting this to an int, why again?

Copy link
Member

@MattToast MattToast Jan 29, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

t.cast is purely for the type checker while the int will actually do the cast at runtime. It's HIGHLY unlikely, but there is the possibility that the "db_cpus" key maps to a float (or a str, or some other type) that can be cast to an int.

so for instance

self.colocated_db_settings = {"db_cpus": 3.0, ...}
db_cpus = int(self.colocated_db_settings.get("db_cpus", 0))
print(db_cpus)

will run just fine and print 3

while

self.colocated_db_settings = {"db_cpus": 3.0, ...}
db_cpus = self.colocated_db_settings.get("db_cpus", 0)
assert isinstance(db_cpus, int)
print(db_cpus)

will raise an AssertionError.

Just to make sure that this is preserving the original behavior and prevent an API break I would rather just subtly lie to the type checker and let it think that self.colocated_db_settings.get("db_cpus", 0) is some type that can be cast to an int. But do LMK if you think I am being too paranoid!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see I see, some witchery I would say

if not db_cpus:
raise ValueError("db_cpus must be configured on colocated_db_settings")

Expand Down
4 changes: 2 additions & 2 deletions tests/install/test_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,14 @@
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.


import pytest

import functools
import pathlib
import platform
import threading
import time

import pytest

import smartsim._core._install.builder as build

# The tests in this file belong to the group_a group
Expand Down
Loading