Skip to content

Commit 9f676d3

Browse files
samluryemeta-codesync[bot]
authored andcommitted
Typed global configuration from python integrated with the Attrs system (#1433)
Summary: Pull Request resolved: #1433 This diff introduces a typed global configuration system in python for monarch, integrated with the hyperactor `Attrs` system and the `hyperactor::global::config` module. To make an attribute configurable via python, first add the meta attribute `PYTHON_CONFIG_KEY`, like so: ``` rootfoo( CONFIG_ENV_VAR = "HYPERACTOR_MESH_DEFAULT_TRANSPORT".to_string(), PYTHON_CONFIG_KEY = "default_transport".to_string(), ) pub attr DEFAULT_TRANSPORT: ChannelTransport = ChannelTransport::Unix; ``` This makes it so that the `DEFAULT_TRANSPORT` attr can be configured using `monarch.configure(default_transport=...)`. In order to ensure that attrs with type `ChannelTransport` can be configured by passing `PyChannelTransport` values to `monarch.configure`, in `monarch_hyperactor/src/config.rs`, add the macro invocation: ``` declare_py_config_type!(PyChannelTransport as ChannelTransport); ``` For attrs with rust types that can be passed directly to and from python (like `String`), simply add: ``` declare_py_config_type!(String); ``` These macro invocations only need to be added once per unique attr type we want to support -- *not* once per actual attr. From python, the `DEFAULT_TRANSPORT` attr is then configurable in the global config by calling ``` monarch._rust_bindings.monarch_hyperactor.config.configure( default_transport=ChannelTransport.MetaTlsWithHostname, ... ) ``` You can then get the currently configured values with ``` monarch._rust_bindings.monarch_hyperactor.config.get_configuration() ``` which returns ``` { "default_transport": ChannelTransport.MetaTlsWithHostname, ... } ``` ghstack-source-id: 314206074 exported-using-ghexport Reviewed By: mariusae Differential Revision: D83701581 fbshipit-source-id: e41f7ccadf28ac20151ca842b6bb0ddfdc23d725
1 parent 2979311 commit 9f676d3

File tree

9 files changed

+350
-6
lines changed

9 files changed

+350
-6
lines changed

hyperactor/src/attrs.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,9 @@ pub struct AttrKeyInfo {
135135
pub parse: fn(&str) -> Result<Box<dyn SerializableValue>, anyhow::Error>,
136136
/// Default value for the attribute, if any.
137137
pub default: Option<&'static dyn SerializableValue>,
138+
/// A reference to the relevant key object with the associated
139+
/// type parameter erased. Can be downcast to a concrete Key<T>.
140+
pub erased: &'static dyn ErasedKey,
138141
}
139142

140143
inventory::collect!(AttrKeyInfo);
@@ -199,6 +202,39 @@ impl<T: 'static> Clone for Key<T> {
199202

200203
impl<T: 'static> Copy for Key<T> {}
201204

205+
/// A trait for type-erased keys.
206+
pub trait ErasedKey: Any + Send + Sync + 'static {
207+
/// The name of the key.
208+
fn name(&self) -> &'static str;
209+
210+
/// The typehash of the key's associated type.
211+
fn typehash(&self) -> u64;
212+
213+
/// The typename of the key's associated type.
214+
fn typename(&self) -> &'static str;
215+
}
216+
217+
impl dyn ErasedKey {
218+
/// Downcast a type-erased key to a specific key type.
219+
pub fn downcast_ref<T: Named + 'static>(&'static self) -> Option<&'static Key<T>> {
220+
(self as &dyn Any).downcast_ref::<Key<T>>()
221+
}
222+
}
223+
224+
impl<T: AttrValue> ErasedKey for Key<T> {
225+
fn name(&self) -> &'static str {
226+
self.name
227+
}
228+
229+
fn typehash(&self) -> u64 {
230+
T::typehash()
231+
}
232+
233+
fn typename(&self) -> &'static str {
234+
T::typename()
235+
}
236+
}
237+
202238
// Enable attr[key] syntax.
203239
impl<T: AttrValue> Index<Key<T>> for Attrs {
204240
type Output = T;
@@ -778,6 +814,7 @@ macro_rules! declare_attrs {
778814
Ok(Box::new(value) as Box<dyn $crate::attrs::SerializableValue>)
779815
},
780816
default: Some($crate::paste! { &[<$name _DEFAULT>] }),
817+
erased: &$name,
781818
}
782819
}
783820
};
@@ -830,6 +867,7 @@ macro_rules! declare_attrs {
830867
Ok(Box::new(value) as Box<dyn $crate::attrs::SerializableValue>)
831868
},
832869
default: None,
870+
erased: &$name,
833871
}
834872
}
835873
};

hyperactor/src/config.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,10 @@ declare_attrs! {
4848
/// key.
4949
pub attr CONFIG_ENV_VAR: String;
5050

51+
/// This is a meta-attribute specifying the name of the kwarg to pass to monarch.configure()
52+
/// to set the attribute value in the global config.
53+
pub attr PYTHON_CONFIG_KEY: String;
54+
5155
/// Maximum frame length for codec
5256
@meta(CONFIG_ENV_VAR = "HYPERACTOR_CODEC_MAX_FRAME_LENGTH".to_string())
5357
pub attr CODEC_MAX_FRAME_LENGTH: usize = 10 * 1024 * 1024 * 1024; // 10 GiB

hyperactor/src/config/global.rs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -229,13 +229,24 @@ pub fn get<T: AttrValue + Copy>(key: Key<T>) -> T {
229229
/// Default. Panics if the key has no default and is not set in
230230
/// any layer.
231231
pub fn get_cloned<T: AttrValue>(key: Key<T>) -> T {
232+
try_get_cloned(key)
233+
.expect("key must have a default")
234+
.clone()
235+
}
236+
237+
/// Try to get a key by cloning the value.
238+
///
239+
/// Resolution order: TestOverride -> Runtime -> Env -> File ->
240+
/// Default. Returns None if the key has no default and is not set in
241+
/// any layer.
242+
pub fn try_get_cloned<T: AttrValue>(key: Key<T>) -> Option<T> {
232243
let layers = LAYERS.read().unwrap();
233244
for layer in &layers.ordered {
234245
if layer.attrs.contains_key(key) {
235-
return layer.attrs.get(key).unwrap().clone();
246+
return layer.attrs.get(key).cloned();
236247
}
237248
}
238-
key.default().expect("key must have a default").clone()
249+
key.default().cloned()
239250
}
240251

241252
/// Insert or replace a configuration layer for the given source.

hyperactor_mesh/src/proc_mesh.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ use hyperactor::channel::ChannelAddr;
3333
use hyperactor::channel::ChannelTransport;
3434
use hyperactor::config;
3535
use hyperactor::config::CONFIG_ENV_VAR;
36+
use hyperactor::config::PYTHON_CONFIG_KEY;
3637
use hyperactor::context;
3738
use hyperactor::declare_attrs;
3839
use hyperactor::mailbox;
@@ -85,10 +86,12 @@ use std::sync::RwLock;
8586

8687
declare_attrs! {
8788
/// Default transport type to use across the application.
88-
@meta(CONFIG_ENV_VAR = "HYPERACTOR_MESH_DEFAULT_TRANSPORT".to_string())
89-
attr DEFAULT_TRANSPORT: ChannelTransport = ChannelTransport::Unix;
89+
@meta(
90+
CONFIG_ENV_VAR = "HYPERACTOR_MESH_DEFAULT_TRANSPORT".to_string(),
91+
PYTHON_CONFIG_KEY = "default_transport".to_string(),
92+
)
93+
pub attr DEFAULT_TRANSPORT: ChannelTransport = ChannelTransport::Unix;
9094
}
91-
9295
/// Get the default transport type to use across the application.
9396
pub fn default_transport() -> ChannelTransport {
9497
config::global::get_cloned(DEFAULT_TRANSPORT)

monarch_hyperactor/src/channel.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ use hyperactor::channel::ChannelTransport;
1313
use hyperactor::channel::MetaTlsAddr;
1414
use hyperactor::channel::TlsMode;
1515
use pyo3::exceptions::PyRuntimeError;
16+
use pyo3::exceptions::PyValueError;
1617
use pyo3::prelude::*;
1718

1819
/// Python binding for [`hyperactor::channel::ChannelTransport`]
@@ -31,6 +32,33 @@ pub enum PyChannelTransport {
3132
// Sim(/*transport:*/ ChannelTransport), TODO kiuk@ add support
3233
}
3334

35+
#[pymethods]
36+
impl PyChannelTransport {
37+
fn get(&self) -> Self {
38+
self.clone()
39+
}
40+
}
41+
42+
impl TryFrom<ChannelTransport> for PyChannelTransport {
43+
type Error = PyErr;
44+
45+
fn try_from(transport: ChannelTransport) -> PyResult<Self> {
46+
match transport {
47+
ChannelTransport::Tcp => Ok(PyChannelTransport::Tcp),
48+
ChannelTransport::MetaTls(TlsMode::Hostname) => {
49+
Ok(PyChannelTransport::MetaTlsWithHostname)
50+
}
51+
ChannelTransport::MetaTls(TlsMode::IpV6) => Ok(PyChannelTransport::MetaTlsWithIpV6),
52+
ChannelTransport::Local => Ok(PyChannelTransport::Local),
53+
ChannelTransport::Unix => Ok(PyChannelTransport::Unix),
54+
_ => Err(PyValueError::new_err(format!(
55+
"unsupported transport: {}",
56+
transport
57+
))),
58+
}
59+
}
60+
}
61+
3462
#[pyclass(
3563
name = "ChannelAddr",
3664
module = "monarch._rust_bindings.monarch_hyperactor.channel"

0 commit comments

Comments
 (0)