Skip to content

Commit b19baa0

Browse files
committed
Cluster(profile_dir=...) loads the profile config
profile config is lower priority than already-loaded config
1 parent 6507d2d commit b19baa0

File tree

3 files changed

+94
-1
lines changed

3 files changed

+94
-1
lines changed

ipyparallel/cluster/cluster.py

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,19 @@
2323

2424
import IPython
2525
import traitlets.log
26+
from IPython.core.profiledir import ProfileDir
2627
from traitlets import Any
2728
from traitlets import Bool
2829
from traitlets import default
2930
from traitlets import Dict
3031
from traitlets import Float
3132
from traitlets import import_item
33+
from traitlets import Instance
3234
from traitlets import Integer
3335
from traitlets import List
3436
from traitlets import Unicode
37+
from traitlets import validate
38+
from traitlets.config import Config
3539
from traitlets.config import LoggingConfigurable
3640

3741
from . import launcher
@@ -267,14 +271,71 @@ def _default_log(self):
267271
else:
268272
return traitlets.log.get_logger()
269273

274+
load_profile = Bool(
275+
True,
276+
config=True,
277+
help="""
278+
If True (default) load ipcluster config from profile directory, if present.
279+
""",
280+
)
270281
# private state
271282
controller = Any()
272283
engines = Dict()
273284

285+
profile_config = Instance(Config, allow_none=False)
286+
287+
@default("profile_config")
288+
def _profile_config_default(self):
289+
"""Load config from our profile"""
290+
if not self.load_profile or not os.path.isdir(self.profile_dir):
291+
# no profile dir, nothing to load
292+
return Config()
293+
294+
from .app import BaseParallelApplication, IPClusterStart
295+
296+
if (
297+
self.parent
298+
and isinstance(self.parent, BaseParallelApplication)
299+
and self.parent.name == 'ipcluster'
300+
and self.parent.profile_dir.location == self.profile_dir
301+
):
302+
# profile config already loaded by parent, nothing new to load
303+
return Config()
304+
305+
self.log.debug(f"Loading profile {self.profile_dir}")
306+
# set profile dir via config
307+
self.config.ProfileDir.location = self.profile_dir
308+
309+
# load profile config via IPCluster
310+
app = IPClusterStart(parent=self, log_level=10)
311+
# adds profile dir to config_files_path
312+
app.init_profile_dir()
313+
# adds system to config_files_path
314+
app.init_config_files()
315+
# actually load the config
316+
app.load_config_file(suppress_errors=False)
317+
return app.config
318+
319+
@validate("config")
320+
def _merge_profile_config(self, proposal):
321+
direct_config = proposal.value
322+
if not self.load_profile:
323+
return direct_config
324+
profile_config = self.profile_config
325+
if not profile_config:
326+
return direct_config
327+
# priority ?! direct > profile
328+
config = Config()
329+
if profile_config:
330+
config.merge(profile_config)
331+
config.merge(direct_config)
332+
return config
333+
274334
def __init__(self, **kwargs):
275335
"""Construct a Cluster"""
276336
if 'parent' not in kwargs and 'config' not in kwargs:
277337
kwargs['parent'] = self._default_parent()
338+
278339
super().__init__(**kwargs)
279340

280341
def __del__(self):

ipyparallel/tests/conftest.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,9 +117,11 @@ def ClusterConstructor(**kwargs):
117117
cfg.EngineLauncher.engine_args = ['--log-level=10']
118118
cfg.ControllerLauncher.controller_args = ['--log-level=10']
119119
kwargs.setdefault("controller_args", ['--ping=250'])
120+
kwargs.setdefault("load_profile", False)
120121

121122
c = ipp.Cluster(**kwargs)
122-
assert c.config is cfg
123+
if not kwargs['load_profile']:
124+
assert c.config == cfg
123125
request.addfinalizer(c.stop_cluster_sync)
124126
return c
125127

ipyparallel/tests/test_cluster.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
import asyncio
2+
import json
23
import os
34
import signal
45
import sys
56
import time
67

78
import pytest
9+
from traitlets.config import Config
810

911
import ipyparallel as ipp
1012
from .clienttest import raises_remote
@@ -183,6 +185,34 @@ def test_sync_with(Cluster):
183185
assert rc[:]['a'] == [5] * 5
184186

185187

188+
def test_load_profile(tmpdir):
189+
profile_dir = tmpdir.join("profile").mkdir()
190+
# config cases:
191+
# - only in profile config (used)
192+
# - in profile config and direct config (direct used)
193+
# - in profile config and kwargs (kwargs used)
194+
with profile_dir.join("ipcluster_config.json").open("w") as f:
195+
json.dump(
196+
{
197+
"Cluster": {
198+
"controller_args": ["--from-profile"],
199+
"n": 5,
200+
"engine_timeout": 10,
201+
}
202+
},
203+
f,
204+
)
205+
print(profile_dir.listdir())
206+
config = Config()
207+
config.Cluster.engine_timeout = 20
208+
c = cluster.Cluster(profile_dir=str(profile_dir), n=10, config=config)
209+
print(c.config)
210+
assert c.profile_dir == str(profile_dir)
211+
assert c.controller_args == ['--from-profile'] # from profile
212+
assert c.engine_timeout == 20 # from config
213+
assert c.n == 10 # from kwarg
214+
215+
186216
@pytest.mark.parametrize(
187217
"classname, expected_class",
188218
[

0 commit comments

Comments
 (0)