Repository: ipython/ipyparallel

Issue:
Documentation for the 'apply_sync' method seems to be missing
Looking at the documentation [here](https://ipyparallel.readthedocs.io/en/6.2.3/api/ipyparallel.html#ipyparallel.DirectView) it looks like essential documentation is missing, for example the usage of the method 

`apply_sync
`

I hope this can be fixed?



Comments:

Patch:
diff --git a/docs/source/api/ipyparallel.rst b/docs/source/api/ipyparallel.rst
index cbab7042..ee7ac089 100644
--- a/docs/source/api/ipyparallel.rst
+++ b/docs/source/api/ipyparallel.rst
@@ -12,16 +12,14 @@ Classes
 -------
 
 .. autoclass:: Client
-    :members:
 
 .. autoclass:: DirectView
-    :members:
 
 .. autoclass:: LoadBalancedView
-    :members:
+
+.. autoclass:: BroadcastView
 
 .. autoclass:: ViewExecutor
-    :members:
 
 Decorators
 ----------
diff --git a/docs/source/conf.py b/docs/source/conf.py
index 8571badb..689c240e 100644
--- a/docs/source/conf.py
+++ b/docs/source/conf.py
@@ -60,6 +60,15 @@ extensions = [
 # You can specify multiple suffix as a list of string:
 source_suffix = ['.rst', '.md']
 
+from traitlets.config import HasTraits
+
+# exclude members inherited from HasTraits by default
+autodoc_default_options = {
+    'members': None,
+    "inherited-members": None,
+    "exclude-members": ','.join(dir(HasTraits)),
+}
+
 # Add dev disclaimer.
 if iprelease['version_info'][-1] == 'dev':
     rst_prolog = """
diff --git a/ipyparallel/client/client.py b/ipyparallel/client/client.py
index 5b688e16..36b4b402 100644
--- a/ipyparallel/client/client.py
+++ b/ipyparallel/client/client.py
@@ -1325,6 +1325,8 @@ class Client(HasTraits):
         if jobs is None:
             # get futures for results
             futures = [f for f in self._futures.values() if hasattr(f, 'output')]
+            if not futures:
+                return
             ar = AsyncResult(self, futures, owner=False)
         else:
             ar = self._asyncresult_from_jobs(jobs, owner=False)
diff --git a/ipyparallel/serialize/canning.py b/ipyparallel/serialize/canning.py
index f2ba4140..b83ef98e 100644
--- a/ipyparallel/serialize/canning.py
+++ b/ipyparallel/serialize/canning.py
@@ -193,6 +193,16 @@ class CannedFunction(CannedObject):
         else:
             self.defaults = None
 
+        if f.__kwdefaults__:
+            self.kwdefaults = can_dict(f.__kwdefaults__)
+        else:
+            self.kwdefaults = None
+
+        if f.__annotations__:
+            self.annotations = can_dict(f.__annotations__)
+        else:
+            self.annotations = None
+
         closure = py3compat.get_closure(f)
         if closure:
             self.closure = tuple(can(cell) for cell in closure)
@@ -218,11 +228,25 @@ class CannedFunction(CannedObject):
             defaults = tuple(uncan(cfd, g) for cfd in self.defaults)
         else:
             defaults = None
+
+        if self.kwdefaults:
+            kwdefaults = uncan_dict(self.kwdefaults)
+        else:
+            kwdefaults = None
+        if self.annotations:
+            annotations = uncan_dict(self.annotations)
+        else:
+            annotations = {}
+
         if self.closure:
             closure = tuple(uncan(cell, g) for cell in self.closure)
         else:
             closure = None
         newFunc = FunctionType(self.code, g, self.__name__, defaults, closure)
+        if kwdefaults:
+            newFunc.__kwdefaults__ = kwdefaults
+        if annotations:
+            newFunc.__annotations__ = annotations
         return newFunc
 
 


Test patch:
diff --git a/ipyparallel/tests/test_canning.py b/ipyparallel/tests/test_canning.py
index f462b343..090cb837 100644
--- a/ipyparallel/tests/test_canning.py
+++ b/ipyparallel/tests/test_canning.py
@@ -21,6 +21,10 @@ def loads(obj):
     return uncan(pickle.loads(obj))
 
 
+def roundtrip(obj):
+    return loads(dumps(obj))
+
+
 def test_no_closure():
     @interactive
     def foo():
@@ -109,3 +113,20 @@ def test_can_partial_buffers():
     loaded.buffers = buffers
     pfoo2 = uncan(loaded)
     assert pfoo2() == partial_foo()
+
+
+def test_keyword_only_arguments():
+    def compute(a, *, b=3):
+        return a * b
+
+    assert compute(2) == 6
+    compute_2 = roundtrip(compute)
+    assert compute_2(2) == 6
+
+
+def test_annotations():
+    def f(a: int):
+        return a
+
+    f2 = roundtrip(f)
+    assert f2.__annotations__ == f.__annotations__