Repository: influxdata/influxdb-client-python

Issue:
`class Point` doesn't support equality comparison
### Specifications

* Client Version: influxdb-client==1.39.0
* InfluxDB Version: irrelevant
* Platform: windows


### Code sample to reproduce problem

```python
def test_point_equality():
    point_a = (
        Point("asd")
        .tag("foo", "bar")
        .field("value", 123.45)
        .time(datetime(2023, 12, 19, 13, 27, 42, 215000, tzinfo=timezone.utc))
    )

    point_b = (
        Point("asd")
        .tag("foo", "bar")
        .field("value", 123.45)
        .time(datetime(2023, 12, 19, 13, 27, 42, 215000, tzinfo=timezone.utc))
    )

    assert point_a == point_b
```


### Expected behavior

Points with equal measurement_name, tags, fields and datetime shall be equal using the `==` comparison.

### Actual behavior

they are not equal:
> >       assert point_a == point_b
> E       assert <influxdb_client.client.write.point.Point object at 0x0000022C05D11110> == <influxdb_client.client.write.point.Point object at 0x0000022C05D11150>

**But**
```python
assert point_a.to_line_protocol() == point_b.to_line_protocol()
```
is fulfilled.

### Additional info

Maybe I'm missing a key concept here why points are not equatable but line protocols are?

Discussion:
codecov-commenter: ## [Codecov](https://app.codecov.io/gh/influxdata/influxdb-client-python/pull/625?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=influxdata) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Comparison is base [(`eb5afd1`)](https://app.codecov.io/gh/influxdata/influxdb-client-python/commit/eb5afd19c2014eddc33247bffef4a360c486277c?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=influxdata) 90.35% compared to head [(`f9a8974`)](https://app.codecov.io/gh/influxdata/influxdb-client-python/pull/625?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=influxdata) 90.36%.

> :exclamation: Current head f9a8974 differs from pull request most recent head 63435b7. Consider uploading reports for the commit 63435b7 to get more accurate results


:exclamation: Your organization needs to install the [Codecov GitHub app](https://github.com/apps/codecov/installations/select_target) to enable full functionality.

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##           master     #625      +/-   ##
==========================================
+ Coverage   90.35%   90.36%   +0.01%     
==========================================
  Files          39       39              
  Lines        3493     3497       +4     
==========================================
+ Hits         3156     3160       +4     
  Misses        337      337              
```



</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/influxdata/influxdb-client-python/pull/625?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=influxdata).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=influxdata).


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 23f7dc2..2f2cb4d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,8 @@
 ## 1.40.0 [unreleased]
 
+### Features
+1. [#625](https://github.com/influxdata/influxdb-client-python/pull/625): Make class `Point` equatable
+
 ### Bug Fixes
 1. [#562](https://github.com/influxdata/influxdb-client-python/pull/562): Use `ThreadPoolScheduler` for `WriteApi`'s batch subject instead of `TimeoutScheduler` to prevent creating unnecessary threads repeatedly
 
diff --git a/influxdb_client/client/write/point.py b/influxdb_client/client/write/point.py
index 60ce7c4..31d44d5 100644
--- a/influxdb_client/client/write/point.py
+++ b/influxdb_client/client/write/point.py
@@ -251,6 +251,18 @@ The output Line protocol will be interpret as a comment by InfluxDB. For more in
         """Create string representation of this Point."""
         return self.to_line_protocol()
 
+    def __eq__(self, other):
+        """Return true iff other is equal to self."""
+        if not isinstance(other, Point):
+            return False
+        # assume points are equal iff their instance fields are equal
+        return (self._tags == other._tags and
+                self._fields == other._fields and
+                self._name == other._name and
+                self._time == other._time and
+                self._write_precision == other._write_precision and
+                self._field_types == other._field_types)
+
 
 def _append_tags(tags):
     _return = []


Test patch:
diff --git a/tests/test_point.py b/tests/test_point.py
index 992ac35..e799ae9 100644
--- a/tests/test_point.py
+++ b/tests/test_point.py
@@ -557,6 +557,110 @@ class PointTest(unittest.TestCase):
             self.assertEqual('#hash_start,location=europe level=2.2', point.to_line_protocol())
         self.assertEqual(1, len(warnings))
 
+    def test_equality_from_dict(self):
+        point_dict = {
+            "measurement": "h2o_feet",
+            "tags": {"location": "coyote_creek"},
+            "fields": {
+                "water_level": 1.0,
+                "some_counter": 108913123234
+            },
+            "field_types": {"some_counter": "float"},
+            "time": 1
+        }
+        point_a = Point.from_dict(point_dict)
+        point_b = Point.from_dict(point_dict)
+        self.assertEqual(point_a, point_b)
+
+    def test_equality(self):
+        # https://github.com/influxdata/influxdb-client-python/issues/623#issue-2048573579
+        point_a = (
+            Point("asd")
+            .tag("foo", "bar")
+            .field("value", 123.45)
+            .time(datetime(2023, 12, 19, 13, 27, 42, 215000, tzinfo=timezone.utc))
+        )
+
+        point_b = (
+            Point("asd")
+            .tag("foo", "bar")
+            .field("value", 123.45)
+            .time(datetime(2023, 12, 19, 13, 27, 42, 215000, tzinfo=timezone.utc))
+        )
+        self.assertEqual(point_a, point_b)
+
+    def test_not_equal_if_tags_differ(self):
+        point_a = (
+            Point("asd")
+            .tag("foo", "bar")
+            .field("value", 123.45)
+            .time(datetime(2023, 12, 19, 13, 27, 42, 215000, tzinfo=timezone.utc))
+        )
+
+        point_b = (
+            Point("asd")
+            .tag("foo", "baz")  # not "bar"
+            .field("value", 123.45)
+            .time(datetime(2023, 12, 19, 13, 27, 42, 215000, tzinfo=timezone.utc))
+        )
+        self.assertNotEqual(point_a, point_b)
+
+    def test_not_equal_if_fields_differ(self):
+        point_a = (
+            Point("asd")
+            .tag("foo", "bar")
+            .field("value", 123.45)
+            .time(datetime(2023, 12, 19, 13, 27, 42, 215000, tzinfo=timezone.utc))
+        )
+
+        point_b = (
+            Point("asd")
+            .tag("foo", "bar")
+            .field("value", 678.90)  # not 123.45
+            .time(datetime(2023, 12, 19, 13, 27, 42, 215000, tzinfo=timezone.utc))
+        )
+        self.assertNotEqual(point_a, point_b)
+
+    def test_not_equal_if_measurements_differ(self):
+        point_a = (
+            Point("asd")
+            .tag("foo", "bar")
+            .field("value", 123.45)
+            .time(datetime(2023, 12, 19, 13, 27, 42, 215000, tzinfo=timezone.utc))
+        )
+
+        point_b = (
+            Point("fgh")  # not "asd"
+            .tag("foo", "bar")
+            .field("value", 123.45)
+            .time(datetime(2023, 12, 19, 13, 27, 42, 215000, tzinfo=timezone.utc))
+        )
+        self.assertNotEqual(point_a, point_b)
+
+    def test_not_equal_if_times_differ(self):
+        point_a = (
+            Point("asd")
+            .tag("foo", "bar")
+            .field("value", 123.45)
+            .time(datetime(2023, 12, 19, 13, 27, 42, 215000, tzinfo=timezone.utc))
+        )
+
+        point_b = (
+            Point("asd")
+            .tag("foo", "bar")
+            .field("value", 123.45)
+            .time(datetime(2024, 12, 19, 13, 27, 42, 215000, tzinfo=timezone.utc))
+        )
+        self.assertNotEqual(point_a, point_b)
+    def test_not_equal_if_other_is_no_point(self):
+        point_a = (
+            Point("asd")
+            .tag("foo", "bar")
+            .field("value", 123.45)
+            .time(datetime(2023, 12, 19, 13, 27, 42, 215000, tzinfo=timezone.utc))
+        )
+        not_a_point = "not a point but a string"
+        self.assertNotEqual(point_a, not_a_point)
 
 if __name__ == '__main__':
     unittest.main()