We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Update Resource merge method precedence for conflict key and empty string
Recently spec has been updated for `Resource`

From [specs](https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/resource/sdk.md#merge)
> The resulting resource MUST have all attributes that are on any of the two input resources. If a key exists on both the old and updating resource, the value of the updating resource MUST be picked (even if the updated value is empty).

This is kind of related to https://github.com/open-telemetry/opentelemetry-python/issues/1526.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `opentelemetry-sdk/src/opentelemetry/sdk/resources/__init__.py`
Content:
```
1 # Copyright The OpenTelemetry Authors
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 #     http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14 
15 """
16 This package implements `OpenTelemetry Resources
17 <https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/resource/sdk.md#resource-sdk>`_:
18 
19     *A Resource is an immutable representation of the entity producing
20     telemetry. For example, a process producing telemetry that is running in
21     a container on Kubernetes has a Pod name, it is in a namespace and
22     possibly is part of a Deployment which also has a name. All three of
23     these attributes can be included in the Resource.*
24 
25 Resource objects are created with `Resource.create`, which accepts attributes
26 (key-values). Resource attributes can also be passed at process invocation in
27 the :envvar:`OTEL_RESOURCE_ATTRIBUTES` environment variable. You should
28 register your resource with the `opentelemetry.sdk.metrics.MeterProvider` and
29 `opentelemetry.sdk.trace.TracerProvider` by passing them into their
30 constructors. The `Resource` passed to a provider is available to the
31 exporter, which can send on this information as it sees fit.
32 
33 .. code-block:: python
34 
35     trace.set_tracer_provider(
36         TracerProvider(
37             resource=Resource.create({
38                 "service.name": "shoppingcart",
39                 "service.instance.id": "instance-12",
40             }),
41         ),
42     )
43     print(trace.get_tracer_provider().resource.attributes)
44 
45     {'telemetry.sdk.language': 'python',
46     'telemetry.sdk.name': 'opentelemetry',
47     'telemetry.sdk.version': '0.13.dev0',
48     'service.name': 'shoppingcart',
49     'service.instance.id': 'instance-12'}
50 
51 Note that the OpenTelemetry project documents certain `"standard attributes"
52 <https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/resource/semantic_conventions/README.md>`_
53 that have prescribed semantic meanings, for example ``service.name`` in the
54 above example.
55 
56 .. envvar:: OTEL_RESOURCE_ATTRIBUTES
57 
58 The :envvar:`OTEL_RESOURCE_ATTRIBUTES` environment variable allows resource
59 attributes to be passed to the SDK at process invocation. The attributes from
60 :envvar:`OTEL_RESOURCE_ATTRIBUTES` are merged with those passed to
61 `Resource.create`, meaning :envvar:`OTEL_RESOURCE_ATTRIBUTES` takes *lower*
62 priority. Attributes should be in the format ``key1=value1,key2=value2``.
63 Additional details are available `in the specification
64 <https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/resource/sdk.md#specifying-resource-information-via-an-environment-variable>`_.
65 
66 .. code-block:: console
67 
68     $ OTEL_RESOURCE_ATTRIBUTES="service.name=shoppingcard,will_be_overridden=foo" python - <<EOF
69     import pprint
70     from opentelemetry.sdk.resources import Resource
71     pprint.pprint(Resource.create({"will_be_overridden": "bar"}).attributes)
72     EOF
73     {'service.name': 'shoppingcard',
74     'telemetry.sdk.language': 'python',
75     'telemetry.sdk.name': 'opentelemetry',
76     'telemetry.sdk.version': '0.13.dev0',
77     'will_be_overridden': 'bar'}
78  """
79 
80 import abc
81 import concurrent.futures
82 import logging
83 import os
84 import typing
85 from json import dumps
86 
87 import pkg_resources
88 
89 LabelValue = typing.Union[str, bool, int, float]
90 Attributes = typing.Dict[str, LabelValue]
91 logger = logging.getLogger(__name__)
92 
93 
94 CLOUD_PROVIDER = "cloud.provider"
95 CLOUD_ACCOUNT_ID = "cloud.account.id"
96 CLOUD_REGION = "cloud.region"
97 CLOUD_ZONE = "cloud.zone"
98 CONTAINER_NAME = "container.name"
99 CONTAINER_ID = "container.id"
100 CONTAINER_IMAGE_NAME = "container.image.name"
101 CONTAINER_IMAGE_TAG = "container.image.tag"
102 DEPLOYMENT_ENVIRONMENT = "deployment.environment"
103 FAAS_NAME = "faas.name"
104 FAAS_ID = "faas.id"
105 FAAS_VERSION = "faas.version"
106 FAAS_INSTANCE = "faas.instance"
107 HOST_NAME = "host.name"
108 HOST_TYPE = "host.type"
109 HOST_IMAGE_NAME = "host.image.name"
110 HOST_IMAGE_ID = "host.image.id"
111 HOST_IMAGE_VERSION = "host.image.version"
112 KUBERNETES_CLUSTER_NAME = "k8s.cluster.name"
113 KUBERNETES_NAMESPACE_NAME = "k8s.namespace.name"
114 KUBERNETES_POD_UID = "k8s.pod.uid"
115 KUBERNETES_POD_NAME = "k8s.pod.name"
116 KUBERNETES_CONTAINER_NAME = "k8s.container.name"
117 KUBERNETES_REPLICA_SET_UID = "k8s.replicaset.uid"
118 KUBERNETES_REPLICA_SET_NAME = "k8s.replicaset.name"
119 KUBERNETES_DEPLOYMENT_UID = "k8s.deployment.uid"
120 KUBERNETES_DEPLOYMENT_NAME = "k8s.deployment.name"
121 KUBERNETES_STATEFUL_SET_UID = "k8s.statefulset.uid"
122 KUBERNETES_STATEFUL_SET_NAME = "k8s.statefulset.name"
123 KUBERNETES_DAEMON_SET_UID = "k8s.daemonset.uid"
124 KUBERNETES_DAEMON_SET_NAME = "k8s.daemonset.name"
125 KUBERNETES_JOB_UID = "k8s.job.uid"
126 KUBERNETES_JOB_NAME = "k8s.job.name"
127 KUBERNETES_CRON_JOB_UID = "k8s.cronjob.uid"
128 KUBERNETES_CRON_JOB_NAME = "k8s.cronjob.name"
129 OS_TYPE = "os.type"
130 OS_DESCRIPTION = "os.description"
131 PROCESS_PID = "process.pid"
132 PROCESS_EXECUTABLE_NAME = "process.executable.name"
133 PROCESS_EXECUTABLE_PATH = "process.executable.path"
134 PROCESS_COMMAND = "process.command"
135 PROCESS_COMMAND_LINE = "process.command_line"
136 PROCESS_COMMAND_ARGS = "process.command_args"
137 PROCESS_OWNER = "process.owner"
138 PROCESS_RUNTIME_NAME = "process.runtime.name"
139 PROCESS_RUNTIME_VERSION = "process.runtime.version"
140 PROCESS_RUNTIME_DESCRIPTION = "process.runtime.description"
141 SERVICE_NAME = "service.name"
142 SERVICE_NAMESPACE = "service.namespace"
143 SERVICE_INSTANCE_ID = "service.instance.id"
144 SERVICE_VERSION = "service.version"
145 TELEMETRY_SDK_NAME = "telemetry.sdk.name"
146 TELEMETRY_SDK_VERSION = "telemetry.sdk.version"
147 TELEMETRY_AUTO_VERSION = "telemetry.auto.version"
148 TELEMETRY_SDK_LANGUAGE = "telemetry.sdk.language"
149 
150 
151 OPENTELEMETRY_SDK_VERSION = pkg_resources.get_distribution(
152     "opentelemetry-sdk"
153 ).version
154 OTEL_RESOURCE_ATTRIBUTES = "OTEL_RESOURCE_ATTRIBUTES"
155 
156 
157 class Resource:
158     def __init__(self, attributes: Attributes):
159         self._attributes = attributes.copy()
160 
161     @staticmethod
162     def create(attributes: typing.Optional[Attributes] = None) -> "Resource":
163         if not attributes:
164             resource = _DEFAULT_RESOURCE
165         else:
166             resource = _DEFAULT_RESOURCE.merge(Resource(attributes))
167         resource = resource.merge(OTELResourceDetector().detect())
168         if not resource.attributes.get(SERVICE_NAME, None):
169             default_service_name = "unknown_service"
170             process_executable_name = resource.attributes.get(
171                 PROCESS_EXECUTABLE_NAME, None
172             )
173             if process_executable_name:
174                 default_service_name += ":" + process_executable_name
175             resource = resource.merge(
176                 Resource({SERVICE_NAME: default_service_name})
177             )
178         return resource
179 
180     @staticmethod
181     def create_empty() -> "Resource":
182         return _EMPTY_RESOURCE
183 
184     @property
185     def attributes(self) -> Attributes:
186         return self._attributes.copy()
187 
188     def merge(self, other: "Resource") -> "Resource":
189         merged_attributes = self.attributes
190         # pylint: disable=protected-access
191         for key, value in other._attributes.items():
192             if key not in merged_attributes or merged_attributes[key] == "":
193                 merged_attributes[key] = value
194         return Resource(merged_attributes)
195 
196     def __eq__(self, other: object) -> bool:
197         if not isinstance(other, Resource):
198             return False
199         return self._attributes == other._attributes
200 
201     def __hash__(self):
202         return hash(dumps(self._attributes, sort_keys=True))
203 
204 
205 _EMPTY_RESOURCE = Resource({})
206 _DEFAULT_RESOURCE = Resource(
207     {
208         TELEMETRY_SDK_LANGUAGE: "python",
209         TELEMETRY_SDK_NAME: "opentelemetry",
210         TELEMETRY_SDK_VERSION: OPENTELEMETRY_SDK_VERSION,
211     }
212 )
213 
214 
215 class ResourceDetector(abc.ABC):
216     def __init__(self, raise_on_error=False):
217         self.raise_on_error = raise_on_error
218 
219     @abc.abstractmethod
220     def detect(self) -> "Resource":
221         raise NotImplementedError()
222 
223 
224 class OTELResourceDetector(ResourceDetector):
225     # pylint: disable=no-self-use
226     def detect(self) -> "Resource":
227         env_resources_items = os.environ.get(OTEL_RESOURCE_ATTRIBUTES)
228         env_resource_map = {}
229         if env_resources_items:
230             env_resource_map = {
231                 key.strip(): value.strip()
232                 for key, value in (
233                     item.split("=") for item in env_resources_items.split(",")
234                 )
235             }
236         return Resource(env_resource_map)
237 
238 
239 def get_aggregated_resources(
240     detectors: typing.List["ResourceDetector"],
241     initial_resource: typing.Optional[Resource] = None,
242     timeout=5,
243 ) -> "Resource":
244     """ Retrieves resources from detectors in the order that they were passed
245 
246     :param detectors: List of resources in order of priority
247     :param initial_resource: Static resource. This has highest priority
248     :param timeout: Number of seconds to wait for each detector to return
249     :return:
250     """
251     final_resource = initial_resource or _EMPTY_RESOURCE
252     detectors = [OTELResourceDetector()] + detectors
253 
254     with concurrent.futures.ThreadPoolExecutor(max_workers=4) as executor:
255         futures = [executor.submit(detector.detect) for detector in detectors]
256         for detector_ind, future in enumerate(futures):
257             detector = detectors[detector_ind]
258             try:
259                 detected_resources = future.result(timeout=timeout)
260             # pylint: disable=broad-except
261             except Exception as ex:
262                 if detector.raise_on_error:
263                     raise ex
264                 logger.warning(
265                     "Exception %s in detector %s, ignoring", ex, detector
266                 )
267                 detected_resources = _EMPTY_RESOURCE
268             finally:
269                 final_resource = final_resource.merge(detected_resources)
270     return final_resource
271
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/resources/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/resources/__init__.py
--- a/opentelemetry-sdk/src/opentelemetry/sdk/resources/__init__.py
+++ b/opentelemetry-sdk/src/opentelemetry/sdk/resources/__init__.py
@@ -155,16 +155,27 @@
 
 
 class Resource:
+    """A Resource is an immutable representation of the entity producing telemetry as Attributes.
+    """
+
     def __init__(self, attributes: Attributes):
         self._attributes = attributes.copy()
 
     @staticmethod
     def create(attributes: typing.Optional[Attributes] = None) -> "Resource":
+        """Creates a new `Resource` from attributes.
+
+        Args:
+            attributes: Optional zero or more key-value pairs.
+
+        Returns:
+            The newly-created Resource.
+        """
         if not attributes:
-            resource = _DEFAULT_RESOURCE
-        else:
-            resource = _DEFAULT_RESOURCE.merge(Resource(attributes))
-        resource = resource.merge(OTELResourceDetector().detect())
+            attributes = {}
+        resource = _DEFAULT_RESOURCE.merge(
+            OTELResourceDetector().detect()
+        ).merge(Resource(attributes))
         if not resource.attributes.get(SERVICE_NAME, None):
             default_service_name = "unknown_service"
             process_executable_name = resource.attributes.get(
@@ -186,11 +197,19 @@
         return self._attributes.copy()
 
     def merge(self, other: "Resource") -> "Resource":
+        """Merges this resource and an updating resource into a new `Resource`.
+
+        If a key exists on both the old and updating resource, the value of the
+        updating resource will override the old resource value.
+
+        Args:
+            other: The other resource to be merged.
+
+        Returns:
+            The newly-created Resource.
+        """
         merged_attributes = self.attributes
-        # pylint: disable=protected-access
-        for key, value in other._attributes.items():
-            if key not in merged_attributes or merged_attributes[key] == "":
-                merged_attributes[key] = value
+        merged_attributes.update(other.attributes)
         return Resource(merged_attributes)
 
     def __eq__(self, other: object) -> bool:

{"golden_diff": "diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/resources/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/resources/__init__.py\n--- a/opentelemetry-sdk/src/opentelemetry/sdk/resources/__init__.py\n+++ b/opentelemetry-sdk/src/opentelemetry/sdk/resources/__init__.py\n@@ -155,16 +155,27 @@\n \n \n class Resource:\n+    \"\"\"A Resource is an immutable representation of the entity producing telemetry as Attributes.\n+    \"\"\"\n+\n     def __init__(self, attributes: Attributes):\n         self._attributes = attributes.copy()\n \n     @staticmethod\n     def create(attributes: typing.Optional[Attributes] = None) -> \"Resource\":\n+        \"\"\"Creates a new `Resource` from attributes.\n+\n+        Args:\n+            attributes: Optional zero or more key-value pairs.\n+\n+        Returns:\n+            The newly-created Resource.\n+        \"\"\"\n         if not attributes:\n-            resource = _DEFAULT_RESOURCE\n-        else:\n-            resource = _DEFAULT_RESOURCE.merge(Resource(attributes))\n-        resource = resource.merge(OTELResourceDetector().detect())\n+            attributes = {}\n+        resource = _DEFAULT_RESOURCE.merge(\n+            OTELResourceDetector().detect()\n+        ).merge(Resource(attributes))\n         if not resource.attributes.get(SERVICE_NAME, None):\n             default_service_name = \"unknown_service\"\n             process_executable_name = resource.attributes.get(\n@@ -186,11 +197,19 @@\n         return self._attributes.copy()\n \n     def merge(self, other: \"Resource\") -> \"Resource\":\n+        \"\"\"Merges this resource and an updating resource into a new `Resource`.\n+\n+        If a key exists on both the old and updating resource, the value of the\n+        updating resource will override the old resource value.\n+\n+        Args:\n+            other: The other resource to be merged.\n+\n+        Returns:\n+            The newly-created Resource.\n+        \"\"\"\n         merged_attributes = self.attributes\n-        # pylint: disable=protected-access\n-        for key, value in other._attributes.items():\n-            if key not in merged_attributes or merged_attributes[key] == \"\":\n-                merged_attributes[key] = value\n+        merged_attributes.update(other.attributes)\n         return Resource(merged_attributes)\n \n     def __eq__(self, other: object) -> bool:\n", "issue": "Update Resource merge method precedence for conflict key and empty string\nRecently spec has been updated for `Resource`\r\n\r\nFrom [specs](https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/resource/sdk.md#merge)\r\n> The resulting resource MUST have all attributes that are on any of the two input resources. If a key exists on both the old and updating resource, the value of the updating resource MUST be picked (even if the updated value is empty).\r\n\r\nThis is kind of related to https://github.com/open-telemetry/opentelemetry-python/issues/1526.\n", "before_files": [{"content": "# Copyright The OpenTelemetry Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"\nThis package implements `OpenTelemetry Resources\n<https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/resource/sdk.md#resource-sdk>`_:\n\n    *A Resource is an immutable representation of the entity producing\n    telemetry. For example, a process producing telemetry that is running in\n    a container on Kubernetes has a Pod name, it is in a namespace and\n    possibly is part of a Deployment which also has a name. All three of\n    these attributes can be included in the Resource.*\n\nResource objects are created with `Resource.create`, which accepts attributes\n(key-values). Resource attributes can also be passed at process invocation in\nthe :envvar:`OTEL_RESOURCE_ATTRIBUTES` environment variable. You should\nregister your resource with the `opentelemetry.sdk.metrics.MeterProvider` and\n`opentelemetry.sdk.trace.TracerProvider` by passing them into their\nconstructors. The `Resource` passed to a provider is available to the\nexporter, which can send on this information as it sees fit.\n\n.. code-block:: python\n\n    trace.set_tracer_provider(\n        TracerProvider(\n            resource=Resource.create({\n                \"service.name\": \"shoppingcart\",\n                \"service.instance.id\": \"instance-12\",\n            }),\n        ),\n    )\n    print(trace.get_tracer_provider().resource.attributes)\n\n    {'telemetry.sdk.language': 'python',\n    'telemetry.sdk.name': 'opentelemetry',\n    'telemetry.sdk.version': '0.13.dev0',\n    'service.name': 'shoppingcart',\n    'service.instance.id': 'instance-12'}\n\nNote that the OpenTelemetry project documents certain `\"standard attributes\"\n<https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/resource/semantic_conventions/README.md>`_\nthat have prescribed semantic meanings, for example ``service.name`` in the\nabove example.\n\n.. envvar:: OTEL_RESOURCE_ATTRIBUTES\n\nThe :envvar:`OTEL_RESOURCE_ATTRIBUTES` environment variable allows resource\nattributes to be passed to the SDK at process invocation. The attributes from\n:envvar:`OTEL_RESOURCE_ATTRIBUTES` are merged with those passed to\n`Resource.create`, meaning :envvar:`OTEL_RESOURCE_ATTRIBUTES` takes *lower*\npriority. Attributes should be in the format ``key1=value1,key2=value2``.\nAdditional details are available `in the specification\n<https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/resource/sdk.md#specifying-resource-information-via-an-environment-variable>`_.\n\n.. code-block:: console\n\n    $ OTEL_RESOURCE_ATTRIBUTES=\"service.name=shoppingcard,will_be_overridden=foo\" python - <<EOF\n    import pprint\n    from opentelemetry.sdk.resources import Resource\n    pprint.pprint(Resource.create({\"will_be_overridden\": \"bar\"}).attributes)\n    EOF\n    {'service.name': 'shoppingcard',\n    'telemetry.sdk.language': 'python',\n    'telemetry.sdk.name': 'opentelemetry',\n    'telemetry.sdk.version': '0.13.dev0',\n    'will_be_overridden': 'bar'}\n \"\"\"\n\nimport abc\nimport concurrent.futures\nimport logging\nimport os\nimport typing\nfrom json import dumps\n\nimport pkg_resources\n\nLabelValue = typing.Union[str, bool, int, float]\nAttributes = typing.Dict[str, LabelValue]\nlogger = logging.getLogger(__name__)\n\n\nCLOUD_PROVIDER = \"cloud.provider\"\nCLOUD_ACCOUNT_ID = \"cloud.account.id\"\nCLOUD_REGION = \"cloud.region\"\nCLOUD_ZONE = \"cloud.zone\"\nCONTAINER_NAME = \"container.name\"\nCONTAINER_ID = \"container.id\"\nCONTAINER_IMAGE_NAME = \"container.image.name\"\nCONTAINER_IMAGE_TAG = \"container.image.tag\"\nDEPLOYMENT_ENVIRONMENT = \"deployment.environment\"\nFAAS_NAME = \"faas.name\"\nFAAS_ID = \"faas.id\"\nFAAS_VERSION = \"faas.version\"\nFAAS_INSTANCE = \"faas.instance\"\nHOST_NAME = \"host.name\"\nHOST_TYPE = \"host.type\"\nHOST_IMAGE_NAME = \"host.image.name\"\nHOST_IMAGE_ID = \"host.image.id\"\nHOST_IMAGE_VERSION = \"host.image.version\"\nKUBERNETES_CLUSTER_NAME = \"k8s.cluster.name\"\nKUBERNETES_NAMESPACE_NAME = \"k8s.namespace.name\"\nKUBERNETES_POD_UID = \"k8s.pod.uid\"\nKUBERNETES_POD_NAME = \"k8s.pod.name\"\nKUBERNETES_CONTAINER_NAME = \"k8s.container.name\"\nKUBERNETES_REPLICA_SET_UID = \"k8s.replicaset.uid\"\nKUBERNETES_REPLICA_SET_NAME = \"k8s.replicaset.name\"\nKUBERNETES_DEPLOYMENT_UID = \"k8s.deployment.uid\"\nKUBERNETES_DEPLOYMENT_NAME = \"k8s.deployment.name\"\nKUBERNETES_STATEFUL_SET_UID = \"k8s.statefulset.uid\"\nKUBERNETES_STATEFUL_SET_NAME = \"k8s.statefulset.name\"\nKUBERNETES_DAEMON_SET_UID = \"k8s.daemonset.uid\"\nKUBERNETES_DAEMON_SET_NAME = \"k8s.daemonset.name\"\nKUBERNETES_JOB_UID = \"k8s.job.uid\"\nKUBERNETES_JOB_NAME = \"k8s.job.name\"\nKUBERNETES_CRON_JOB_UID = \"k8s.cronjob.uid\"\nKUBERNETES_CRON_JOB_NAME = \"k8s.cronjob.name\"\nOS_TYPE = \"os.type\"\nOS_DESCRIPTION = \"os.description\"\nPROCESS_PID = \"process.pid\"\nPROCESS_EXECUTABLE_NAME = \"process.executable.name\"\nPROCESS_EXECUTABLE_PATH = \"process.executable.path\"\nPROCESS_COMMAND = \"process.command\"\nPROCESS_COMMAND_LINE = \"process.command_line\"\nPROCESS_COMMAND_ARGS = \"process.command_args\"\nPROCESS_OWNER = \"process.owner\"\nPROCESS_RUNTIME_NAME = \"process.runtime.name\"\nPROCESS_RUNTIME_VERSION = \"process.runtime.version\"\nPROCESS_RUNTIME_DESCRIPTION = \"process.runtime.description\"\nSERVICE_NAME = \"service.name\"\nSERVICE_NAMESPACE = \"service.namespace\"\nSERVICE_INSTANCE_ID = \"service.instance.id\"\nSERVICE_VERSION = \"service.version\"\nTELEMETRY_SDK_NAME = \"telemetry.sdk.name\"\nTELEMETRY_SDK_VERSION = \"telemetry.sdk.version\"\nTELEMETRY_AUTO_VERSION = \"telemetry.auto.version\"\nTELEMETRY_SDK_LANGUAGE = \"telemetry.sdk.language\"\n\n\nOPENTELEMETRY_SDK_VERSION = pkg_resources.get_distribution(\n    \"opentelemetry-sdk\"\n).version\nOTEL_RESOURCE_ATTRIBUTES = \"OTEL_RESOURCE_ATTRIBUTES\"\n\n\nclass Resource:\n    def __init__(self, attributes: Attributes):\n        self._attributes = attributes.copy()\n\n    @staticmethod\n    def create(attributes: typing.Optional[Attributes] = None) -> \"Resource\":\n        if not attributes:\n            resource = _DEFAULT_RESOURCE\n        else:\n            resource = _DEFAULT_RESOURCE.merge(Resource(attributes))\n        resource = resource.merge(OTELResourceDetector().detect())\n        if not resource.attributes.get(SERVICE_NAME, None):\n            default_service_name = \"unknown_service\"\n            process_executable_name = resource.attributes.get(\n                PROCESS_EXECUTABLE_NAME, None\n            )\n            if process_executable_name:\n                default_service_name += \":\" + process_executable_name\n            resource = resource.merge(\n                Resource({SERVICE_NAME: default_service_name})\n            )\n        return resource\n\n    @staticmethod\n    def create_empty() -> \"Resource\":\n        return _EMPTY_RESOURCE\n\n    @property\n    def attributes(self) -> Attributes:\n        return self._attributes.copy()\n\n    def merge(self, other: \"Resource\") -> \"Resource\":\n        merged_attributes = self.attributes\n        # pylint: disable=protected-access\n        for key, value in other._attributes.items():\n            if key not in merged_attributes or merged_attributes[key] == \"\":\n                merged_attributes[key] = value\n        return Resource(merged_attributes)\n\n    def __eq__(self, other: object) -> bool:\n        if not isinstance(other, Resource):\n            return False\n        return self._attributes == other._attributes\n\n    def __hash__(self):\n        return hash(dumps(self._attributes, sort_keys=True))\n\n\n_EMPTY_RESOURCE = Resource({})\n_DEFAULT_RESOURCE = Resource(\n    {\n        TELEMETRY_SDK_LANGUAGE: \"python\",\n        TELEMETRY_SDK_NAME: \"opentelemetry\",\n        TELEMETRY_SDK_VERSION: OPENTELEMETRY_SDK_VERSION,\n    }\n)\n\n\nclass ResourceDetector(abc.ABC):\n    def __init__(self, raise_on_error=False):\n        self.raise_on_error = raise_on_error\n\n    @abc.abstractmethod\n    def detect(self) -> \"Resource\":\n        raise NotImplementedError()\n\n\nclass OTELResourceDetector(ResourceDetector):\n    # pylint: disable=no-self-use\n    def detect(self) -> \"Resource\":\n        env_resources_items = os.environ.get(OTEL_RESOURCE_ATTRIBUTES)\n        env_resource_map = {}\n        if env_resources_items:\n            env_resource_map = {\n                key.strip(): value.strip()\n                for key, value in (\n                    item.split(\"=\") for item in env_resources_items.split(\",\")\n                )\n            }\n        return Resource(env_resource_map)\n\n\ndef get_aggregated_resources(\n    detectors: typing.List[\"ResourceDetector\"],\n    initial_resource: typing.Optional[Resource] = None,\n    timeout=5,\n) -> \"Resource\":\n    \"\"\" Retrieves resources from detectors in the order that they were passed\n\n    :param detectors: List of resources in order of priority\n    :param initial_resource: Static resource. This has highest priority\n    :param timeout: Number of seconds to wait for each detector to return\n    :return:\n    \"\"\"\n    final_resource = initial_resource or _EMPTY_RESOURCE\n    detectors = [OTELResourceDetector()] + detectors\n\n    with concurrent.futures.ThreadPoolExecutor(max_workers=4) as executor:\n        futures = [executor.submit(detector.detect) for detector in detectors]\n        for detector_ind, future in enumerate(futures):\n            detector = detectors[detector_ind]\n            try:\n                detected_resources = future.result(timeout=timeout)\n            # pylint: disable=broad-except\n            except Exception as ex:\n                if detector.raise_on_error:\n                    raise ex\n                logger.warning(\n                    \"Exception %s in detector %s, ignoring\", ex, detector\n                )\n                detected_resources = _EMPTY_RESOURCE\n            finally:\n                final_resource = final_resource.merge(detected_resources)\n    return final_resource\n", "path": "opentelemetry-sdk/src/opentelemetry/sdk/resources/__init__.py"}], "after_files": [{"content": "# Copyright The OpenTelemetry Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"\nThis package implements `OpenTelemetry Resources\n<https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/resource/sdk.md#resource-sdk>`_:\n\n    *A Resource is an immutable representation of the entity producing\n    telemetry. For example, a process producing telemetry that is running in\n    a container on Kubernetes has a Pod name, it is in a namespace and\n    possibly is part of a Deployment which also has a name. All three of\n    these attributes can be included in the Resource.*\n\nResource objects are created with `Resource.create`, which accepts attributes\n(key-values). Resource attributes can also be passed at process invocation in\nthe :envvar:`OTEL_RESOURCE_ATTRIBUTES` environment variable. You should\nregister your resource with the `opentelemetry.sdk.metrics.MeterProvider` and\n`opentelemetry.sdk.trace.TracerProvider` by passing them into their\nconstructors. The `Resource` passed to a provider is available to the\nexporter, which can send on this information as it sees fit.\n\n.. code-block:: python\n\n    trace.set_tracer_provider(\n        TracerProvider(\n            resource=Resource.create({\n                \"service.name\": \"shoppingcart\",\n                \"service.instance.id\": \"instance-12\",\n            }),\n        ),\n    )\n    print(trace.get_tracer_provider().resource.attributes)\n\n    {'telemetry.sdk.language': 'python',\n    'telemetry.sdk.name': 'opentelemetry',\n    'telemetry.sdk.version': '0.13.dev0',\n    'service.name': 'shoppingcart',\n    'service.instance.id': 'instance-12'}\n\nNote that the OpenTelemetry project documents certain `\"standard attributes\"\n<https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/resource/semantic_conventions/README.md>`_\nthat have prescribed semantic meanings, for example ``service.name`` in the\nabove example.\n\n.. envvar:: OTEL_RESOURCE_ATTRIBUTES\n\nThe :envvar:`OTEL_RESOURCE_ATTRIBUTES` environment variable allows resource\nattributes to be passed to the SDK at process invocation. The attributes from\n:envvar:`OTEL_RESOURCE_ATTRIBUTES` are merged with those passed to\n`Resource.create`, meaning :envvar:`OTEL_RESOURCE_ATTRIBUTES` takes *lower*\npriority. Attributes should be in the format ``key1=value1,key2=value2``.\nAdditional details are available `in the specification\n<https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/resource/sdk.md#specifying-resource-information-via-an-environment-variable>`_.\n\n.. code-block:: console\n\n    $ OTEL_RESOURCE_ATTRIBUTES=\"service.name=shoppingcard,will_be_overridden=foo\" python - <<EOF\n    import pprint\n    from opentelemetry.sdk.resources import Resource\n    pprint.pprint(Resource.create({\"will_be_overridden\": \"bar\"}).attributes)\n    EOF\n    {'service.name': 'shoppingcard',\n    'telemetry.sdk.language': 'python',\n    'telemetry.sdk.name': 'opentelemetry',\n    'telemetry.sdk.version': '0.13.dev0',\n    'will_be_overridden': 'bar'}\n \"\"\"\n\nimport abc\nimport concurrent.futures\nimport logging\nimport os\nimport typing\nfrom json import dumps\n\nimport pkg_resources\n\nLabelValue = typing.Union[str, bool, int, float]\nAttributes = typing.Dict[str, LabelValue]\nlogger = logging.getLogger(__name__)\n\n\nCLOUD_PROVIDER = \"cloud.provider\"\nCLOUD_ACCOUNT_ID = \"cloud.account.id\"\nCLOUD_REGION = \"cloud.region\"\nCLOUD_ZONE = \"cloud.zone\"\nCONTAINER_NAME = \"container.name\"\nCONTAINER_ID = \"container.id\"\nCONTAINER_IMAGE_NAME = \"container.image.name\"\nCONTAINER_IMAGE_TAG = \"container.image.tag\"\nDEPLOYMENT_ENVIRONMENT = \"deployment.environment\"\nFAAS_NAME = \"faas.name\"\nFAAS_ID = \"faas.id\"\nFAAS_VERSION = \"faas.version\"\nFAAS_INSTANCE = \"faas.instance\"\nHOST_NAME = \"host.name\"\nHOST_TYPE = \"host.type\"\nHOST_IMAGE_NAME = \"host.image.name\"\nHOST_IMAGE_ID = \"host.image.id\"\nHOST_IMAGE_VERSION = \"host.image.version\"\nKUBERNETES_CLUSTER_NAME = \"k8s.cluster.name\"\nKUBERNETES_NAMESPACE_NAME = \"k8s.namespace.name\"\nKUBERNETES_POD_UID = \"k8s.pod.uid\"\nKUBERNETES_POD_NAME = \"k8s.pod.name\"\nKUBERNETES_CONTAINER_NAME = \"k8s.container.name\"\nKUBERNETES_REPLICA_SET_UID = \"k8s.replicaset.uid\"\nKUBERNETES_REPLICA_SET_NAME = \"k8s.replicaset.name\"\nKUBERNETES_DEPLOYMENT_UID = \"k8s.deployment.uid\"\nKUBERNETES_DEPLOYMENT_NAME = \"k8s.deployment.name\"\nKUBERNETES_STATEFUL_SET_UID = \"k8s.statefulset.uid\"\nKUBERNETES_STATEFUL_SET_NAME = \"k8s.statefulset.name\"\nKUBERNETES_DAEMON_SET_UID = \"k8s.daemonset.uid\"\nKUBERNETES_DAEMON_SET_NAME = \"k8s.daemonset.name\"\nKUBERNETES_JOB_UID = \"k8s.job.uid\"\nKUBERNETES_JOB_NAME = \"k8s.job.name\"\nKUBERNETES_CRON_JOB_UID = \"k8s.cronjob.uid\"\nKUBERNETES_CRON_JOB_NAME = \"k8s.cronjob.name\"\nOS_TYPE = \"os.type\"\nOS_DESCRIPTION = \"os.description\"\nPROCESS_PID = \"process.pid\"\nPROCESS_EXECUTABLE_NAME = \"process.executable.name\"\nPROCESS_EXECUTABLE_PATH = \"process.executable.path\"\nPROCESS_COMMAND = \"process.command\"\nPROCESS_COMMAND_LINE = \"process.command_line\"\nPROCESS_COMMAND_ARGS = \"process.command_args\"\nPROCESS_OWNER = \"process.owner\"\nPROCESS_RUNTIME_NAME = \"process.runtime.name\"\nPROCESS_RUNTIME_VERSION = \"process.runtime.version\"\nPROCESS_RUNTIME_DESCRIPTION = \"process.runtime.description\"\nSERVICE_NAME = \"service.name\"\nSERVICE_NAMESPACE = \"service.namespace\"\nSERVICE_INSTANCE_ID = \"service.instance.id\"\nSERVICE_VERSION = \"service.version\"\nTELEMETRY_SDK_NAME = \"telemetry.sdk.name\"\nTELEMETRY_SDK_VERSION = \"telemetry.sdk.version\"\nTELEMETRY_AUTO_VERSION = \"telemetry.auto.version\"\nTELEMETRY_SDK_LANGUAGE = \"telemetry.sdk.language\"\n\n\nOPENTELEMETRY_SDK_VERSION = pkg_resources.get_distribution(\n    \"opentelemetry-sdk\"\n).version\nOTEL_RESOURCE_ATTRIBUTES = \"OTEL_RESOURCE_ATTRIBUTES\"\n\n\nclass Resource:\n    \"\"\"A Resource is an immutable representation of the entity producing telemetry as Attributes.\n    \"\"\"\n\n    def __init__(self, attributes: Attributes):\n        self._attributes = attributes.copy()\n\n    @staticmethod\n    def create(attributes: typing.Optional[Attributes] = None) -> \"Resource\":\n        \"\"\"Creates a new `Resource` from attributes.\n\n        Args:\n            attributes: Optional zero or more key-value pairs.\n\n        Returns:\n            The newly-created Resource.\n        \"\"\"\n        if not attributes:\n            attributes = {}\n        resource = _DEFAULT_RESOURCE.merge(\n            OTELResourceDetector().detect()\n        ).merge(Resource(attributes))\n        if not resource.attributes.get(SERVICE_NAME, None):\n            default_service_name = \"unknown_service\"\n            process_executable_name = resource.attributes.get(\n                PROCESS_EXECUTABLE_NAME, None\n            )\n            if process_executable_name:\n                default_service_name += \":\" + process_executable_name\n            resource = resource.merge(\n                Resource({SERVICE_NAME: default_service_name})\n            )\n        return resource\n\n    @staticmethod\n    def create_empty() -> \"Resource\":\n        return _EMPTY_RESOURCE\n\n    @property\n    def attributes(self) -> Attributes:\n        return self._attributes.copy()\n\n    def merge(self, other: \"Resource\") -> \"Resource\":\n        \"\"\"Merges this resource and an updating resource into a new `Resource`.\n\n        If a key exists on both the old and updating resource, the value of the\n        updating resource will override the old resource value.\n\n        Args:\n            other: The other resource to be merged.\n\n        Returns:\n            The newly-created Resource.\n        \"\"\"\n        merged_attributes = self.attributes\n        merged_attributes.update(other.attributes)\n        return Resource(merged_attributes)\n\n    def __eq__(self, other: object) -> bool:\n        if not isinstance(other, Resource):\n            return False\n        return self._attributes == other._attributes\n\n    def __hash__(self):\n        return hash(dumps(self._attributes, sort_keys=True))\n\n\n_EMPTY_RESOURCE = Resource({})\n_DEFAULT_RESOURCE = Resource(\n    {\n        TELEMETRY_SDK_LANGUAGE: \"python\",\n        TELEMETRY_SDK_NAME: \"opentelemetry\",\n        TELEMETRY_SDK_VERSION: OPENTELEMETRY_SDK_VERSION,\n    }\n)\n\n\nclass ResourceDetector(abc.ABC):\n    def __init__(self, raise_on_error=False):\n        self.raise_on_error = raise_on_error\n\n    @abc.abstractmethod\n    def detect(self) -> \"Resource\":\n        raise NotImplementedError()\n\n\nclass OTELResourceDetector(ResourceDetector):\n    # pylint: disable=no-self-use\n    def detect(self) -> \"Resource\":\n        env_resources_items = os.environ.get(OTEL_RESOURCE_ATTRIBUTES)\n        env_resource_map = {}\n        if env_resources_items:\n            env_resource_map = {\n                key.strip(): value.strip()\n                for key, value in (\n                    item.split(\"=\") for item in env_resources_items.split(\",\")\n                )\n            }\n        return Resource(env_resource_map)\n\n\ndef get_aggregated_resources(\n    detectors: typing.List[\"ResourceDetector\"],\n    initial_resource: typing.Optional[Resource] = None,\n    timeout=5,\n) -> \"Resource\":\n    \"\"\" Retrieves resources from detectors in the order that they were passed\n\n    :param detectors: List of resources in order of priority\n    :param initial_resource: Static resource. This has highest priority\n    :param timeout: Number of seconds to wait for each detector to return\n    :return:\n    \"\"\"\n    final_resource = initial_resource or _EMPTY_RESOURCE\n    detectors = [OTELResourceDetector()] + detectors\n\n    with concurrent.futures.ThreadPoolExecutor(max_workers=4) as executor:\n        futures = [executor.submit(detector.detect) for detector in detectors]\n        for detector_ind, future in enumerate(futures):\n            detector = detectors[detector_ind]\n            try:\n                detected_resources = future.result(timeout=timeout)\n            # pylint: disable=broad-except\n            except Exception as ex:\n                if detector.raise_on_error:\n                    raise ex\n                logger.warning(\n                    \"Exception %s in detector %s, ignoring\", ex, detector\n                )\n                detected_resources = _EMPTY_RESOURCE\n            finally:\n                final_resource = final_resource.merge(detected_resources)\n    return final_resource\n", "path": "opentelemetry-sdk/src/opentelemetry/sdk/resources/__init__.py"}]}