Repository: open-telemetry/opentelemetry-python

Issue:
Schema URL is null for some messages and "" for others
While working on the getting started guide, I notice the console exporter will sometimes output empty string and sometimes `null` for schema URL:

```json
{
  "resource_metrics": [
    {
      "resource": {
        "attributes": {
          "telemetry.sdk.language": "python",
          "telemetry.sdk.name": "opentelemetry",
          "telemetry.sdk.version": "1.12.0rc1",
          "telemetry.auto.version": "0.31b0",
          "service.name": "unknown_service"
        },
        "schema_url": ""
      },
      "scope_metrics": [
        {
          "scope": {
            "name": "app",
            "version": "",
            "schema_url": null
          },
          "metrics": [
            {
              "name": "roll_counter",
              "description": "The number of rolls by roll value",
              "unit": "",
              "data": {
                "data_points": [
                  {
                    "attributes": {
                      "roll.value": 4
                    },
                    "start_time_unix_nano": 1654790325350232600,
                    "time_unix_nano": 1654790332211598800,
                    "value": 3
                  },
                  {
                    "attributes": {
                      "roll.value": 6
                    },
                    "start_time_unix_nano": 1654790325350232600,
                    "time_unix_nano": 1654790332211598800,
                    "value": 4
                  },
                  {
                    "attributes": {
                      "roll.value": 5
                    },
                    "start_time_unix_nano": 1654790325350232600,
                    "time_unix_nano": 1654790332211598800,
                    "value": 1
                  },
                  {
                    "attributes": {
                      "roll.value": 1
                    },
                    "start_time_unix_nano": 1654790325350232600,
                    "time_unix_nano": 1654790332211598800,
                    "value": 2
                  },
                  {
                    "attributes": {
                      "roll.value": 3
                    },
                    "start_time_unix_nano": 1654790325350232600,
                    "time_unix_nano": 1654790332211598800,
                    "value": 1
                  }
                ],
                "aggregation_temporality": 2,
                "is_monotonic": true
              }
            }
          ],
          "schema_url": null
        }
      ],
      "schema_url": ""
    }
  ]
}
```

Note, in OTLP protobuf these two are equivalent. I think we should have consistent treatment of the empty value for these in the SDK though.


Discussion:

Resolving patch:
diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/util/instrumentation.py b/opentelemetry-sdk/src/opentelemetry/sdk/util/instrumentation.py
--- a/opentelemetry-sdk/src/opentelemetry/sdk/util/instrumentation.py
+++ b/opentelemetry-sdk/src/opentelemetry/sdk/util/instrumentation.py
@@ -35,6 +35,8 @@ def __init__(
     ):
         self._name = name
         self._version = version
+        if schema_url is None:
+            schema_url = ""
         self._schema_url = schema_url
 
     def __repr__(self):
@@ -90,6 +92,8 @@ def __init__(
     ) -> None:
         self._name = name
         self._version = version
+        if schema_url is None:
+            schema_url = ""
         self._schema_url = schema_url
 
     def __repr__(self) -> str:


Test patch:
diff --git a/opentelemetry-sdk/tests/trace/test_trace.py b/opentelemetry-sdk/tests/trace/test_trace.py
--- a/opentelemetry-sdk/tests/trace/test_trace.py
+++ b/opentelemetry-sdk/tests/trace/test_trace.py
@@ -271,7 +271,7 @@ def test_invalid_instrumentation_info(self):
         )
         span1 = tracer1.start_span("foo")
         self.assertTrue(span1.is_recording())
-        self.assertEqual(tracer1.instrumentation_info.schema_url, None)
+        self.assertEqual(tracer1.instrumentation_info.schema_url, "")
         self.assertEqual(tracer1.instrumentation_info.version, "")
         self.assertEqual(tracer1.instrumentation_info.name, "")
 
@@ -280,7 +280,7 @@ def test_invalid_instrumentation_info(self):
         )
         span2 = tracer2.start_span("bar")
         self.assertTrue(span2.is_recording())
-        self.assertEqual(tracer2.instrumentation_info.schema_url, None)
+        self.assertEqual(tracer2.instrumentation_info.schema_url, "")
         self.assertEqual(tracer2.instrumentation_info.version, "")
         self.assertEqual(tracer2.instrumentation_info.name, "")