diff --git a/IPython/parallel/client/client.py b/IPython/parallel/client/client.py index a81c9d9..8caa458 100644 --- a/IPython/parallel/client/client.py +++ b/IPython/parallel/client/client.py @@ -1032,8 +1032,14 @@ class Client(HasTraits): def direct_view(self, targets='all'): """construct a DirectView object. - If no targets are specified, create a DirectView - using all engines. + If no targets are specified, create a DirectView using all engines. + + rc.direct_view('all') is distinguished from rc[:] in that 'all' will + evaluate the target engines at each execution, whereas rc[:] will connect to + all *current* engines, and that list will not change. + + That is, 'all' will always use all engines, whereas rc[:] will not use + engines added after the DirectView is constructed. Parameters ---------- diff --git a/IPython/parallel/client/remotefunction.py b/IPython/parallel/client/remotefunction.py index 7a1a134..f7850f6 100644 --- a/IPython/parallel/client/remotefunction.py +++ b/IPython/parallel/client/remotefunction.py @@ -140,6 +140,8 @@ class ParallelFunction(RemoteFunction): self.mapObject = mapClass() def __call__(self, *sequences): + client = self.view.client + # check that the length of sequences match len_0 = len(sequences[0]) for s in sequences: @@ -158,11 +160,12 @@ class ParallelFunction(RemoteFunction): warnings.warn("`chunksize` is ignored unless load balancing", UserWarning) # multiplexed: targets = self.view.targets + # 'all' is lazily evaluated at execution time, which is now: + if targets == 'all': + targets = client._build_targets(targets)[1] nparts = len(targets) msg_ids = [] - # my_f = lambda *a: map(self.func, *a) - client = self.view.client for index, t in enumerate(targets): args = [] for seq in sequences: diff --git a/IPython/parallel/tests/test_client.py b/IPython/parallel/tests/test_client.py index 1fe856b..9ad18b8 100644 --- a/IPython/parallel/tests/test_client.py +++ b/IPython/parallel/tests/test_client.py @@ -79,7 +79,7 @@ class TestClient(ClusterTestCase): self.assertEquals(v.targets, None) def test_dview_targets(self): - """test load_balanced_view targets""" + """test direct_view targets""" v = self.client.direct_view() self.assertEquals(v.targets, 'all') v = self.client.direct_view('all') @@ -87,6 +87,41 @@ class TestClient(ClusterTestCase): v = self.client.direct_view(-1) self.assertEquals(v.targets, self.client.ids[-1]) + def test_lazy_all_targets(self): + """test lazy evaluation of rc.direct_view('all')""" + v = self.client.direct_view() + self.assertEquals(v.targets, 'all') + + def double(x): + return x*2 + seq = range(100) + ref = [ double(x) for x in seq ] + + # add some engines, which should be used + self.add_engines(2) + n1 = len(self.client.ids) + + # simple apply + r = v.apply_sync(lambda : 1) + self.assertEquals(r, [1] * n1) + + # map goes through remotefunction + r = v.map_sync(double, seq) + self.assertEquals(r, ref) + + # add a couple more engines, and try again + self.add_engines(2) + n2 = len(self.client.ids) + self.assertNotEquals(n2, n1) + + # apply + r = v.apply_sync(lambda : 1) + self.assertEquals(r, [1] * n2) + + # map + r = v.map_sync(double, seq) + self.assertEquals(r, ref) + def test_targets(self): """test various valid targets arguments""" build = self.client._build_targets