Repository: dask/distributed

Issue:
ENH: add client.get_versions(check=True)
xref https://github.com/dask/distributed/pull/596

if check == True (default), then raise an error message showing differences in versions (e.g. distributed between workers and scheduler). makes debugging versions differences much easier.

open to different reporting options back to user (e.g. maybe just print out diffs)


Discussion:

Resolving patch:
diff --git a/distributed/client.py b/distributed/client.py
--- a/distributed/client.py
+++ b/distributed/client.py
@@ -1975,9 +1975,15 @@ def scheduler_info(self):
         """
         return sync(self.loop, self.scheduler.identity)
 
-    def get_versions(self):
+    def get_versions(self, check=False):
         """ Return version info for the scheduler, all workers and myself
 
+        Parameters
+        ----------
+        check : boolean, default False
+            raise ValueError if all required & optional packages
+            do not match
+
         Examples
         --------
         >>> c.get_versions()  # doctest: +SKIP
@@ -1998,7 +2004,36 @@ def f(worker=None):
                 return None
 
         workers = sync(self.loop, self._run, f)
-        return {'scheduler': scheduler, 'workers': workers, 'client': client}
+        result = {'scheduler': scheduler, 'workers': workers, 'client': client}
+
+        if check:
+            # we care about the required & optional packages matching
+            extract = lambda x: merge(result[x]['packages'].values())
+            client_versions = extract('client')
+            scheduler_versions = extract('scheduler')
+
+            for pkg, cv in client_versions.items():
+                sv = scheduler_versions[pkg]
+                if sv != cv:
+                    raise ValueError("package [{package}] is version [{client}] "
+                                     "on client and [{scheduler}] on scheduler!".format(
+                                         package=pkg,
+                                         client=cv,
+                                         scheduler=sv))
+
+            for w, d in workers.items():
+                worker_versions = merge(d['packages'].values())
+                for pkg, cv in client_versions.items():
+                    wv = worker_versions[pkg]
+                    if wv != cv:
+                        raise ValueError("package [{package}] is version [{client}] "
+                                         "on client and [{worker}] on worker [{w}]!".format(
+                                             package=pkg,
+                                             client=cv,
+                                             worker=wv,
+                                             w=w))
+
+        return result
 
     def futures_of(self, futures):
         return futures_of(futures, client=self)


Test patch:
diff --git a/distributed/tests/test_client.py b/distributed/tests/test_client.py
--- a/distributed/tests/test_client.py
+++ b/distributed/tests/test_client.py
@@ -3281,6 +3281,10 @@ def test_get_versions(loop):
             for k, v in v['workers'].items():
                 assert v is not None
 
+            c.get_versions(check=True)
+            # smoke test for versions
+            # that this does not raise
+
 
 def test_threaded_get_within_distributed(loop):
     with cluster() as (s, [a, b]):