Repository: googleapis/proto-plus-python

Issue:
TypeError: 'NoneType' object is not iterable when testing equality of repeated composite or scalar
#### Steps to reproduce

Repeated Composite

```
>>> import proto
>>> class Foo(proto.Message):
...     bar = proto.Field(proto.INT32, number=1)
... 
>>> class Baz(proto.Message):
...     foos = proto.RepeatedField(proto.MESSAGE, message=Foo, number=1)
... 
>>> baz = Baz(foos=[Foo(bar=42)])
>>> assert baz.foos != None

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/google/home/partheniou/git/proto-plus-python/proto/marshal/collections/repeated.py", line 63, in __ne__
    return not self == other
  File "/usr/local/google/home/partheniou/git/proto-plus-python/proto/marshal/collections/repeated.py", line 121, in __eq__
    if super().__eq__(other):
  File "/usr/local/google/home/partheniou/git/proto-plus-python/proto/marshal/collections/repeated.py", line 52, in __eq__
    return tuple(self.pb) == tuple(other)
TypeError: 'NoneType' object is not iterable
```


Repeated Scalar

```
>>> import proto
>>> class Foo(proto.Message):
...     bar = proto.RepeatedField(proto.INT32, number=1)
... 
>>> foo = Foo(bar=[1, 1, 2, 3, 5, 8, 13])
>>> assert foo.bar != None

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/google/home/partheniou/git/proto-plus-python/proto/marshal/collections/repeated.py", line 63, in __ne__
    return not self == other
  File "/usr/local/google/home/partheniou/git/proto-plus-python/proto/marshal/collections/repeated.py", line 52, in __eq__
    return tuple(self.pb) == tuple(other)
TypeError: 'NoneType' object is not iterable
```

Hints from issue comments:

Solution patch:
diff --git a/proto/marshal/collections/repeated.py b/proto/marshal/collections/repeated.py
index 29bacff..a656041 100644
--- a/proto/marshal/collections/repeated.py
+++ b/proto/marshal/collections/repeated.py
@@ -14,6 +14,7 @@
 
 import collections
 import copy
+from typing import Iterable
 
 from proto.utils import cached_property
 
@@ -48,7 +49,7 @@ class Repeated(collections.abc.MutableSequence):
     def __eq__(self, other):
         if hasattr(other, "pb"):
             return tuple(self.pb) == tuple(other.pb)
-        return tuple(self.pb) == tuple(other)
+        return tuple(self.pb) == tuple(other) if isinstance(other, Iterable) else False
 
     def __getitem__(self, key):
         """Return the given item."""
@@ -119,7 +120,11 @@ class RepeatedComposite(Repeated):
     def __eq__(self, other):
         if super().__eq__(other):
             return True
-        return tuple([i for i in self]) == tuple(other)
+        return (
+            tuple([i for i in self]) == tuple(other)
+            if isinstance(other, Iterable)
+            else False
+        )
 
     def __getitem__(self, key):
         return self._marshal.to_python(self._pb_type, self.pb[key])


Test patch:
diff --git a/tests/test_fields_repeated_composite.py b/tests/test_fields_repeated_composite.py
index db6be27..7c9f73e 100644
--- a/tests/test_fields_repeated_composite.py
+++ b/tests/test_fields_repeated_composite.py
@@ -47,6 +47,7 @@ def test_repeated_composite_equality():
 
     baz = Baz(foos=[Foo(bar=42)])
     assert baz.foos == baz.foos
+    assert baz.foos != None
 
 
 def test_repeated_composite_init_struct():
diff --git a/tests/test_fields_repeated_scalar.py b/tests/test_fields_repeated_scalar.py
index e07cf13..d638313 100644
--- a/tests/test_fields_repeated_scalar.py
+++ b/tests/test_fields_repeated_scalar.py
@@ -72,6 +72,7 @@ def test_repeated_scalar_eq_ne():
     foo = Foo(bar=[1, 1, 2, 3, 5, 8, 13])
     assert foo.bar == copy.copy(foo.bar)
     assert foo.bar != [1, 2, 4, 8, 16]
+    assert foo.bar != None
 
 
 def test_repeated_scalar_del():