Repository: pynamodb/PynamoDB

Issue:
Some expressions require conversion to Path or else they won't work
Looks like doing something like this will result in a boolean result (instead of an pynamodb.expressions.condition.Comparison)
```
(RModel.base_price != RModel.res_price.amount)
```
The workaround is to use Path() like this
```
(Path(RModel.base_price) != Path(RModel.res_price.amount))
```

Discussion:

Patch:
diff --git a/pynamodb/attributes.py b/pynamodb/attributes.py
index cfd9a39..7b116d2 100644
--- a/pynamodb/attributes.py
+++ b/pynamodb/attributes.py
@@ -143,17 +143,9 @@ class Attribute(Generic[_T]):
 
     # Condition Expression Support
     def __eq__(self, other: Any) -> 'Comparison':  # type: ignore[override]
-        if isinstance(other, MapAttribute) and other._is_attribute_container():
-            return Path(self).__eq__(other)
-        if other is None or isinstance(other, Attribute):  # handle object identity comparison
-            return self is other  # type: ignore
         return Path(self).__eq__(other)
 
     def __ne__(self, other: Any) -> 'Comparison':  # type: ignore[override]
-        if isinstance(other, MapAttribute) and other._is_attribute_container():
-            return Path(self).__ne__(other)
-        if other is None or isinstance(other, Attribute):  # handle object identity comparison
-            return self is not other  # type: ignore
         return Path(self).__ne__(other)
 
     def __lt__(self, other: Any) -> 'Comparison':
@@ -868,14 +860,34 @@ class MapAttribute(Attribute[Mapping[_KT, _VT]], AttributeContainer):
 
     def __eq__(self, other: Any) -> 'Comparison':  # type: ignore[override]
         if self._is_attribute_container():
-            return self is other  # type: ignore
+            return NotImplemented
         return Attribute.__eq__(self, other)
 
     def __ne__(self, other: Any) -> 'Comparison':  # type: ignore[override]
         if self._is_attribute_container():
-            return self is not other  # type: ignore
+            return NotImplemented
         return Attribute.__ne__(self, other)
 
+    def __lt__(self, other: Any) -> 'Comparison':
+        if self._is_attribute_container():
+            return NotImplemented
+        return Attribute.__lt__(self, other)
+
+    def __le__(self, other: Any) -> 'Comparison':
+        if self._is_attribute_container():
+            return NotImplemented
+        return Attribute.__le__(self, other)
+
+    def __gt__(self, other: Any) -> 'Comparison':
+        if self._is_attribute_container():
+            return NotImplemented
+        return Attribute.__gt__(self, other)
+
+    def __ge__(self, other: Any) -> 'Comparison':
+        if self._is_attribute_container():
+            return NotImplemented
+        return Attribute.__ge__(self, other)
+
     def __iter__(self):
         if self._is_attribute_container():
             return iter(self.attribute_values)


Test patch:
diff --git a/tests/test_expressions.py b/tests/test_expressions.py
index a683f6b..d9456e5 100644
--- a/tests/test_expressions.py
+++ b/tests/test_expressions.py
@@ -340,6 +340,18 @@ class ConditionExpressionTestCase(TestCase):
         assert self.placeholder_names == {'foo': '#0'}
         assert self.expression_attribute_values == {':0': {'M': {'bar': {'S': 'baz'}}}}
 
+    def test_map_comparison_rhs(self):
+        # Simulate initialization from inside an AttributeContainer
+        my_map_attribute = MapAttribute[str, str](attr_name='foo')
+        my_map_attribute._make_attribute()
+        my_map_attribute._update_attribute_paths(my_map_attribute.attr_name)
+
+        condition = MapAttribute(bar='baz') == my_map_attribute
+        expression = condition.serialize(self.placeholder_names, self.expression_attribute_values)
+        assert expression == "#0 = :0"
+        assert self.placeholder_names == {'foo': '#0'}
+        assert self.expression_attribute_values == {':0': {'M': {'bar': {'S': 'baz'}}}}
+
     def test_list_comparison(self):
         condition = ListAttribute(attr_name='foo') == ['bar', 'baz']
         expression = condition.serialize(self.placeholder_names, self.expression_attribute_values)
@@ -409,6 +421,13 @@ class ConditionExpressionTestCase(TestCase):
         with self.assertRaises(AttributeError):
             _ = my_map_attribute['missing_attribute'] == 'baz'
 
+    def test_attribute_comparison(self):
+        condition = self.attribute == UnicodeAttribute(attr_name='bar')
+        expression = condition.serialize(self.placeholder_names, self.expression_attribute_values)
+        assert expression == "#0 = #1"
+        assert self.placeholder_names == {'foo': '#0', 'bar': '#1'}
+        assert self.expression_attribute_values == {}
+
 
 class UpdateExpressionTestCase(TestCase):