Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions monarch_hyperactor/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,13 @@ pub struct PyInstance {
pub(crate) rank: PyPoint,
#[pyo3(get, set, name = "_children")]
children: Option<PyObject>,

#[pyo3(get, set, name = "name")]
name: String,
#[pyo3(get, set, name = "class_name")]
class_name: Option<String>,
#[pyo3(get, set, name = "creator")]
creator: Option<PyObject>,
}

#[pymethods]
Expand Down Expand Up @@ -171,6 +178,9 @@ impl<I: Into<ContextInstance>> From<I> for PyInstance {
controller_controller: None,
rank: PyPoint::new(0, Extent::unity().into()),
children: None,
name: "root".to_string(),
class_name: None,
creator: None,
}
}
}
Expand Down
71 changes: 69 additions & 2 deletions python/monarch/_src/actor/actor_mesh.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,12 @@ def proc(self) -> "ProcMesh":
rank: Point
proc_mesh: "ProcMesh"
_controller_controller: "_ControllerController"
name: str # the name this actor was given on spawn
class_name: str # the fully qualified class name of the actor.
creator: Optional[
"CreatorInstance"
] # information about the actor who spawned this actor
# None if this actor is the spawning actor.

# this property is used to hold the handles to actors and processes launched by this actor
# in order to keep them alive until this actor exits.
Expand All @@ -179,6 +185,47 @@ def _as_rust(self) -> HyInstance:
def _as_py(ins: HyInstance) -> "Instance":
return cast(Instance, ins)

def _as_creator(self) -> "CreatorInstance":
return CreatorInstance(
self.rank,
self.proc_mesh,
self.proc,
self.name,
self.class_name,
self.creator,
)

def __repr__(self) -> str:
return _qualified_name(self)


@dataclass
class CreatorInstance:
"""
An instance that can be serialized so it can be passed around
to describe the creation hierarchy of an actor instance.
"""

rank: Point
proc_mesh: "ProcMesh"
proc: "ProcMesh"
name: str
class_name: Optional[str]
creator: Optional["CreatorInstance"]

def __repr__(self) -> str:
return _qualified_name(self)


def _qualified_name(ins: "CreatorInstance | Instance | None") -> str:
names = []
while ins:
class_prefix = "" if ins.class_name is None else f"{ins.class_name} "
rank_postfix = str(ins.rank) if len(ins.rank) > 0 else ""
names.append(f"<{class_prefix}{ins.name}{rank_postfix}>")
ins = ins.creator
return ".".join(reversed(names))


@rust_struct("monarch_hyperactor::context::Context")
class Context:
Expand Down Expand Up @@ -881,8 +928,16 @@ async def handle(
match method:
case MethodSpecifier.Init():
ins = ctx.actor_instance
Class, ins.proc_mesh, ins._controller_controller, *args = args
(
Class,
ins.proc_mesh,
ins._controller_controller,
ins.name,
ins.creator,
*args,
) = args
ins.rank = ctx.message_rank
ins.class_name = f"{Class.__module__}.{Class.__qualname__}"
try:
self.instance = Class(*args, **kwargs)
self._maybe_exit_debugger()
Expand Down Expand Up @@ -1183,6 +1238,7 @@ def _endpoint(
def _create(
cls,
Class: Type[T],
name: str,
actor_mesh: "PythonActorMesh",
shape: Shape,
proc_mesh: "ProcMesh",
Expand All @@ -1205,7 +1261,18 @@ async def null_func(*_args: Iterable[Any], **_kwargs: Dict[str, Any]) -> None:
None,
False,
)
send(ep, (mesh._class, proc_mesh, controller_controller, *args), kwargs)
send(
ep,
(
mesh._class,
proc_mesh,
controller_controller,
name,
context().actor_instance._as_creator(),
*args,
),
kwargs,
)

return mesh

Expand Down
1 change: 1 addition & 0 deletions python/monarch/_src/actor/proc_mesh.py
Original file line number Diff line number Diff line change
Expand Up @@ -463,6 +463,7 @@ def _spawn_nonblocking_on(
actor_mesh = HyProcMeshV0.spawn_async(pm, name, _Actor)
service = ActorMesh._create(
Class,
name,
actor_mesh,
self._shape,
self,
Expand Down
11 changes: 8 additions & 3 deletions python/monarch/_src/actor/python_extension_methods.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,14 @@ def __call__(self, python_class: Type[T]) -> Type[T]:
raise ValueError(f"mismatched type names {rust_name} != {python_name}")
for name, implementation in python_class.__dict__.items():
if hasattr(self.rust_class, name):
# do not patch in the stub methods that
# are already defined by the rust implementation
continue
the_attr = getattr(self.rust_class, name)
is_object_default = name.startswith("__") and getattr(
the_attr, "__qualname__", ""
).startswith("object.")
if not is_object_default:
# do not patch in the stub methods that
# are already defined by the rust implementation
continue
if not callable(implementation) and not isinstance(
implementation, property
):
Expand Down
1 change: 1 addition & 0 deletions python/monarch/_src/actor/v1/proc_mesh.py
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,7 @@ def _spawn_nonblocking_on(
)
service = ActorMesh._create(
Class,
name,
actor_mesh,
self._region.as_shape(),
self,
Expand Down
22 changes: 21 additions & 1 deletion python/tests/test_python_actors.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
import unittest.mock
from tempfile import TemporaryDirectory
from types import ModuleType
from typing import cast, Tuple
from typing import Any, cast, Tuple

import monarch.actor
import pytest
Expand Down Expand Up @@ -1731,3 +1731,23 @@ def test_setup_async() -> None:
counter.incr.call().get()
# Make sure no errors occur in the meantime
time.sleep(10)


class Named(Actor):
@endpoint
def report(self) -> Any:
return context().actor_instance.creator, str(context().actor_instance)


def test_instance_name():
cr, result = (
this_host()
.spawn_procs(per_host={"f": 2})
.spawn("the_name", Named)
.slice(f=0)
.report.call_one()
.get()
)
assert "test_python_actors.Named the_name{'f': 0/2}>" in result
assert cr.name == "root"
assert str(context().actor_instance) == "<root>"
Loading