Repository: influxdata/influxdb-client-python

Issue:
Warn on measurement starting with hashtags
<!--

Thank you for suggesting an idea to improve this client. 

* Please add a :+1: or comment on a similar existing feature request instead of opening a new one.
    * https://github.com/influxdata/influxdb-client-python/issues?utf8=%E2%9C%93&q=is%3Aissue+is%3Aopen+is%3Aclosed+sort%3Aupdated-desc+label%3A%22enhancement%22+

-->

__Proposal:__
Warn when a measurement name starts with a #.

__Current behavior:__
When a measurement starts with #, it is silently ignored. When such a name is written in the line protocol, this line is ignored since the # at the start of a line marks a comment.

__Desired behavior:__
Either find a way to escape the # or warn that such a measurement cannot be used.

__Use case:__
We have measurement names generated from data that can start with a #. The behavior that they are just missing is confusing and we didn't register it at first.


Discussion:
bednar: I think it can also help to the reporter because the `write_api` uses a `Point` to write `dictionary`: https://github.com/influxdata/influxdb-client-python/blob/540a2dc725ba0bb677203e2ac1ede132b23dba4f/influxdb_client/client/write/point.py#L61

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4c336bb..09bbb80 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -13,6 +13,7 @@
 
 ### Others
 1. [#472](https://github.com/influxdata/influxdb-client-python/pull/472): Drop supports for Python 3.6
+1. [#495](https://github.com/influxdata/influxdb-client-python/pull/495): Add warning for measurement name starts with `#`
 
 ### Documentation
 1. [#397](https://github.com/influxdata/influxdb-client-python/pull/397): Add an example: How to use RxPY to prepare batches by maximum bytes count
diff --git a/influxdb_client/client/write/point.py b/influxdb_client/client/write/point.py
index 3b9e543..3bee403 100644
--- a/influxdb_client/client/write/point.py
+++ b/influxdb_client/client/write/point.py
@@ -1,6 +1,7 @@
 """Point data structure to represent LineProtocol."""
 
 import math
+import warnings
 from builtins import int
 from datetime import datetime, timedelta, timezone
 from decimal import Decimal
@@ -181,6 +182,13 @@ class Point(object):
          :param precision: required precision of LineProtocol. If it's not set then use the precision from ``Point``.
         """
         _measurement = _escape_key(self._name, _ESCAPE_MEASUREMENT)
+        if _measurement.startswith("#"):
+            message = f"""The measurement name '{_measurement}' start with '#'.
+
+The output Line protocol will be interpret as a comment by InfluxDB. For more info see:
+    - https://docs.influxdata.com/influxdb/latest/reference/syntax/line-protocol/#comments
+"""
+            warnings.warn(message, SyntaxWarning)
         _tags = _append_tags(self._tags)
         _fields = _append_fields(self._fields)
         if not _fields:
@@ -249,26 +257,26 @@ def _append_fields(fields):
     return f"{','.join(_return)}"
 
 
-def _append_time(time, write_precision):
+def _append_time(time, write_precision) -> str:
     if time is None:
         return ''
     return f" {int(_convert_timestamp(time, write_precision))}"
 
 
-def _escape_key(tag, escape_list=None):
+def _escape_key(tag, escape_list=None) -> str:
     if escape_list is None:
         escape_list = _ESCAPE_KEY
     return str(tag).translate(escape_list)
 
 
-def _escape_tag_value(value):
+def _escape_tag_value(value) -> str:
     ret = _escape_key(value)
     if ret.endswith('\\'):
         ret += ' '
     return ret
 
 
-def _escape_string(value):
+def _escape_string(value) -> str:
     return str(value).translate(_ESCAPE_STRING)
 
 


Test patch:
diff --git a/tests/test_point.py b/tests/test_point.py
index 4b08c6c..51552f0 100644
--- a/tests/test_point.py
+++ b/tests/test_point.py
@@ -4,6 +4,7 @@ import unittest
 from datetime import datetime, timezone, timedelta
 from decimal import Decimal
 
+import pytest
 from dateutil import tz
 
 from influxdb_client import Point, WritePrecision
@@ -494,6 +495,12 @@ class PointTest(unittest.TestCase):
                                 record_field_keys=["pressure", "temperature"])
         self.assertEqual("custom_sensor_id,location=warehouse_125 pressure=125i", point.to_line_protocol())
 
+    def test_name_start_with_hash(self):
+        point = Point.measurement("#hash_start").tag("location", "europe").field("level", 2.2)
+        with pytest.warns(SyntaxWarning) as warnings:
+            self.assertEqual('#hash_start,location=europe level=2.2', point.to_line_protocol())
+        self.assertEqual(1, len(warnings))
+
 
 if __name__ == '__main__':
     unittest.main()