Repository: OpenMined/PySyft

Issue:
More descriptive error (or warning) when workers can't see each other?
In the Part 4 of the tutorial available at examples/tutorials/ there is this block of code:

```
bob.add_workers([alice, secure_worker])
alice.add_workers([bob, secure_worker])
secure_worker.add_workers([alice, bob])
```

If you remove these lines on cell 6 while trying to perform a move operation you see the following error:

```
/syft-0.1.0-py3.6.egg/syft/core/frameworks/torch/tensor.py in register_pointer(self)
   1015     def register_pointer(self):
   1016         worker = self.owner
-> 1017         location = self.location.id
   1018         id_at_location = self.id_at_location
   1019         # Add the remote address

AttributeError: 'str' object has no attribute 'id'
```

Which is not a very descriptive message and does not indicate the real problem.

The same error message shows on Part 3 if you remove these  lines:

```
# making sure that bob/alice know about each other
bob.add_worker(alice)
alice.add_worker(bob)
```

And try to run something like:
```
x = sy.FloatTensor([1,2,3,4,5]).send(bob).send(alice)
```

---

I think a more descriptive error message can help users debug this simple mistake, would be happy to add this.

Cheers!


Discussion:

Resolving patch:
diff --git a/syft/core/frameworks/torch/hook.py b/syft/core/frameworks/torch/hook.py
--- a/syft/core/frameworks/torch/hook.py
+++ b/syft/core/frameworks/torch/hook.py
@@ -686,7 +686,7 @@ def module_end_get_(self):
         def module_move_(self, dest):
             return self.send(dest).end_get()
 
-        torch.nn.Module.move =  module_move_
+        torch.nn.Module.move = module_move_
 
         def module_get_(self):
             """Get model parameters"""
diff --git a/syft/core/frameworks/torch/tensor.py b/syft/core/frameworks/torch/tensor.py
--- a/syft/core/frameworks/torch/tensor.py
+++ b/syft/core/frameworks/torch/tensor.py
@@ -2632,7 +2632,7 @@ def send(self, *workers, ptr_id=None, as_list=False):
             for worker in workers:
                 gpt_dict[worker] = (self * 1).send(worker).child
             sy._GeneralizedPointerTensor(gpt_dict).on(self)
-            if(as_list):
+            if as_list:
                 return self.pointers()
             else:
                 return self
@@ -2737,7 +2737,7 @@ def send(
         new_data_id=None,
         new_grad_id=None,
         new_grad_data_id=None,
-        as_list=False
+        as_list=False,
     ):
         """Give the root of the chain held by self to worker self->alice->obj
         [worker] => self->worker->alice->obj Because there are Variable
@@ -2761,7 +2761,7 @@ def send(
                 gpt_dict[worker] = (self * 1).send(worker).child
 
             sy._GeneralizedPointerTensor(gpt_dict).on(self)
-            if(as_list):
+            if as_list:
                 return self.child.pointers()
             return self
 
@@ -2801,8 +2801,10 @@ def send(
                 wrapper.native_set_()
             wrapper.child.id = id
             pointer = wrapper.child.create_pointer(
-                location=worker, id_at_location=remote_id, register=True,
-                original_pointer=original_pointer
+                location=worker,
+                id_at_location=remote_id,
+                register=True,
+                original_pointer=original_pointer,
             )
             torch_utils.bind_tensor_nodes(wrapper, pointer)
 
diff --git a/syft/core/workers/base.py b/syft/core/workers/base.py
--- a/syft/core/workers/base.py
+++ b/syft/core/workers/base.py
@@ -607,10 +607,11 @@ def get_worker(self, id_or_worker):
             if id_or_worker in self._known_workers:
                 return self._known_workers[id_or_worker]
             else:
-                logging.warning(
-                    "Worker", self.id, "couldnt recognize worker", id_or_worker
+                raise RuntimeWarning(
+                    "Worker {} couldnt recognize worker {}".format(
+                        self.id, id_or_worker
+                    )
                 )
-                return id_or_worker
         else:
             if id_or_worker.id not in self._known_workers:
                 self.add_worker(id_or_worker)


Test patch:
diff --git a/test/torch_test.py b/test/torch_test.py
--- a/test/torch_test.py
+++ b/test/torch_test.py
@@ -359,6 +359,17 @@ def test_send_get_tensor(self):
         # because .get_() was called, x should no longer be in the remote worker's objects dict
         assert ptr_id not in bob._objects
 
+    def test_send_pointer_to_unknown_worker(self):
+        """Tests that sending a pointer to a unknown worker results on a
+        RuntimeWarning exception."""
+        # Create worker that doesn't know any other worker
+        carl = sy.VirtualWorker(id="carl", hook=hook, is_client_worker=False)
+        try:
+            sy.FloatTensor([1, 2, 3, 4, 5]).send(bob).send(carl)
+            assert False
+        except RuntimeWarning:
+            assert True
+
     def test_multiple_pointers_to_same_target(self):
         # There are two cases:
         #   - You're sending a var on a loc:id you're already pointing at -> should abort
@@ -658,7 +669,11 @@ def test_end_get_tensor(self):
 
         bob_id = random.randint(0, 10e10)
         alice_id = random.randint(0, 10e10)
-        x = sy.FloatTensor([1, 2, 3, 4, 5]).send(bob, ptr_id=bob_id).send(alice, ptr_id=alice_id)
+        x = (
+            sy.FloatTensor([1, 2, 3, 4, 5])
+            .send(bob, ptr_id=bob_id)
+            .send(alice, ptr_id=alice_id)
+        )
 
         x2 = x.end_get()
         # Now alice will own the tensor that was in bob and bob won't have it anymore
@@ -1958,5 +1973,6 @@ def test_gpc_workers(self):
 
         assert results == [k.id for k in x_pointer_tensor_dict.keys()]
 
+
 if __name__ == "__main__":
     unittest.main()