We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Implement InMemoryMetricExporter
See [spec](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/sdk_exporters/in-memory.md). This will be great for testing.

IMO this should be a "pull exporter" (metric reader atm) that has a method `get_metrics()` or similar to return metrics from the SDK.

--- 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/_metrics/export/__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 import logging
16 import os
17 from abc import ABC, abstractmethod
18 from enum import Enum
19 from os import environ, linesep
20 from sys import stdout
21 from threading import Event, Thread
22 from typing import IO, Callable, Iterable, Optional, Sequence
23 
24 from opentelemetry.context import (
25     _SUPPRESS_INSTRUMENTATION_KEY,
26     attach,
27     detach,
28     set_value,
29 )
30 from opentelemetry.sdk._metrics.metric_reader import MetricReader
31 from opentelemetry.sdk._metrics.point import AggregationTemporality, Metric
32 from opentelemetry.util._once import Once
33 
34 _logger = logging.getLogger(__name__)
35 
36 
37 class MetricExportResult(Enum):
38     SUCCESS = 0
39     FAILURE = 1
40 
41 
42 class MetricExporter(ABC):
43     """Interface for exporting metrics.
44 
45     Interface to be implemented by services that want to export metrics received
46     in their own format.
47     """
48 
49     @property
50     def preferred_temporality(self) -> AggregationTemporality:
51         return AggregationTemporality.CUMULATIVE
52 
53     @abstractmethod
54     def export(self, metrics: Sequence[Metric]) -> "MetricExportResult":
55         """Exports a batch of telemetry data.
56 
57         Args:
58             metrics: The list of `opentelemetry.sdk._metrics.data.MetricData` objects to be exported
59 
60         Returns:
61             The result of the export
62         """
63 
64     @abstractmethod
65     def shutdown(self) -> None:
66         """Shuts down the exporter.
67 
68         Called when the SDK is shut down.
69         """
70 
71 
72 class ConsoleMetricExporter(MetricExporter):
73     """Implementation of :class:`MetricExporter` that prints metrics to the
74     console.
75 
76     This class can be used for diagnostic purposes. It prints the exported
77     metrics to the console STDOUT.
78     """
79 
80     def __init__(
81         self,
82         out: IO = stdout,
83         formatter: Callable[[Metric], str] = lambda metric: metric.to_json()
84         + linesep,
85     ):
86         self.out = out
87         self.formatter = formatter
88 
89     def export(self, metrics: Sequence[Metric]) -> MetricExportResult:
90         for metric in metrics:
91             self.out.write(self.formatter(metric))
92         self.out.flush()
93         return MetricExportResult.SUCCESS
94 
95     def shutdown(self) -> None:
96         pass
97 
98 
99 class PeriodicExportingMetricReader(MetricReader):
100     """`PeriodicExportingMetricReader` is an implementation of `MetricReader`
101     that collects metrics based on a user-configurable time interval, and passes the
102     metrics to the configured exporter.
103     """
104 
105     def __init__(
106         self,
107         exporter: MetricExporter,
108         export_interval_millis: Optional[float] = None,
109         export_timeout_millis: Optional[float] = None,
110     ) -> None:
111         super().__init__(preferred_temporality=exporter.preferred_temporality)
112         self._exporter = exporter
113         if export_interval_millis is None:
114             try:
115                 export_interval_millis = float(
116                     environ.get("OTEL_METRIC_EXPORT_INTERVAL", 60000)
117                 )
118             except ValueError:
119                 _logger.warning(
120                     "Found invalid value for export interval, using default"
121                 )
122                 export_interval_millis = 60000
123         if export_timeout_millis is None:
124             try:
125                 export_timeout_millis = float(
126                     environ.get("OTEL_METRIC_EXPORT_TIMEOUT", 30000)
127                 )
128             except ValueError:
129                 _logger.warning(
130                     "Found invalid value for export timeout, using default"
131                 )
132                 export_timeout_millis = 30000
133         self._export_interval_millis = export_interval_millis
134         self._export_timeout_millis = export_timeout_millis
135         self._shutdown = False
136         self._shutdown_event = Event()
137         self._shutdown_once = Once()
138         self._daemon_thread = Thread(target=self._ticker, daemon=True)
139         self._daemon_thread.start()
140         if hasattr(os, "register_at_fork"):
141             os.register_at_fork(
142                 after_in_child=self._at_fork_reinit
143             )  # pylint: disable=protected-access
144 
145     def _at_fork_reinit(self):
146         self._daemon_thread = Thread(target=self._ticker, daemon=True)
147         self._daemon_thread.start()
148 
149     def _ticker(self) -> None:
150         interval_secs = self._export_interval_millis / 1e3
151         while not self._shutdown_event.wait(interval_secs):
152             self.collect()
153         # one last collection below before shutting down completely
154         self.collect()
155 
156     def _receive_metrics(self, metrics: Iterable[Metric]) -> None:
157         if metrics is None:
158             return
159         token = attach(set_value(_SUPPRESS_INSTRUMENTATION_KEY, True))
160         try:
161             self._exporter.export(metrics)
162         except Exception as e:  # pylint: disable=broad-except,invalid-name
163             _logger.exception("Exception while exporting metrics %s", str(e))
164         detach(token)
165 
166     def shutdown(self) -> bool:
167         def _shutdown():
168             self._shutdown = True
169 
170         did_set = self._shutdown_once.do_once(_shutdown)
171         if not did_set:
172             _logger.warning("Can't shutdown multiple times")
173             return False
174 
175         self._shutdown_event.set()
176         self._daemon_thread.join()
177         self._exporter.shutdown()
178         return True
179
```
--- 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/_metrics/export/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/_metrics/export/__init__.py
--- a/opentelemetry-sdk/src/opentelemetry/sdk/_metrics/export/__init__.py
+++ b/opentelemetry-sdk/src/opentelemetry/sdk/_metrics/export/__init__.py
@@ -18,8 +18,8 @@
 from enum import Enum
 from os import environ, linesep
 from sys import stdout
-from threading import Event, Thread
-from typing import IO, Callable, Iterable, Optional, Sequence
+from threading import Event, RLock, Thread
+from typing import IO, Callable, Iterable, List, Optional, Sequence
 
 from opentelemetry.context import (
     _SUPPRESS_INSTRUMENTATION_KEY,
@@ -96,6 +96,36 @@
         pass
 
 
+class InMemoryMetricReader(MetricReader):
+    """Implementation of :class:`MetricReader` that returns its metrics from :func:`metrics`.
+
+    This is useful for e.g. unit tests.
+    """
+
+    def __init__(
+        self,
+        preferred_temporality: AggregationTemporality = AggregationTemporality.CUMULATIVE,
+    ) -> None:
+        super().__init__(preferred_temporality=preferred_temporality)
+        self._lock = RLock()
+        self._metrics: List[Metric] = []
+
+    def get_metrics(self) -> List[Metric]:
+        """Reads and returns current metrics from the SDK"""
+        with self._lock:
+            self.collect()
+            metrics = self._metrics
+            self._metrics = []
+        return metrics
+
+    def _receive_metrics(self, metrics: Iterable[Metric]):
+        with self._lock:
+            self._metrics = list(metrics)
+
+    def shutdown(self) -> bool:
+        return True
+
+
 class PeriodicExportingMetricReader(MetricReader):
     """`PeriodicExportingMetricReader` is an implementation of `MetricReader`
     that collects metrics based on a user-configurable time interval, and passes the

{"golden_diff": "diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/_metrics/export/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/_metrics/export/__init__.py\n--- a/opentelemetry-sdk/src/opentelemetry/sdk/_metrics/export/__init__.py\n+++ b/opentelemetry-sdk/src/opentelemetry/sdk/_metrics/export/__init__.py\n@@ -18,8 +18,8 @@\n from enum import Enum\n from os import environ, linesep\n from sys import stdout\n-from threading import Event, Thread\n-from typing import IO, Callable, Iterable, Optional, Sequence\n+from threading import Event, RLock, Thread\n+from typing import IO, Callable, Iterable, List, Optional, Sequence\n \n from opentelemetry.context import (\n     _SUPPRESS_INSTRUMENTATION_KEY,\n@@ -96,6 +96,36 @@\n         pass\n \n \n+class InMemoryMetricReader(MetricReader):\n+    \"\"\"Implementation of :class:`MetricReader` that returns its metrics from :func:`metrics`.\n+\n+    This is useful for e.g. unit tests.\n+    \"\"\"\n+\n+    def __init__(\n+        self,\n+        preferred_temporality: AggregationTemporality = AggregationTemporality.CUMULATIVE,\n+    ) -> None:\n+        super().__init__(preferred_temporality=preferred_temporality)\n+        self._lock = RLock()\n+        self._metrics: List[Metric] = []\n+\n+    def get_metrics(self) -> List[Metric]:\n+        \"\"\"Reads and returns current metrics from the SDK\"\"\"\n+        with self._lock:\n+            self.collect()\n+            metrics = self._metrics\n+            self._metrics = []\n+        return metrics\n+\n+    def _receive_metrics(self, metrics: Iterable[Metric]):\n+        with self._lock:\n+            self._metrics = list(metrics)\n+\n+    def shutdown(self) -> bool:\n+        return True\n+\n+\n class PeriodicExportingMetricReader(MetricReader):\n     \"\"\"`PeriodicExportingMetricReader` is an implementation of `MetricReader`\n     that collects metrics based on a user-configurable time interval, and passes the\n", "issue": "Implement InMemoryMetricExporter\nSee [spec](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/sdk_exporters/in-memory.md). This will be great for testing.\r\n\r\nIMO this should be a \"pull exporter\" (metric reader atm) that has a method `get_metrics()` or similar to return metrics from the SDK.\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\nimport logging\nimport os\nfrom abc import ABC, abstractmethod\nfrom enum import Enum\nfrom os import environ, linesep\nfrom sys import stdout\nfrom threading import Event, Thread\nfrom typing import IO, Callable, Iterable, Optional, Sequence\n\nfrom opentelemetry.context import (\n    _SUPPRESS_INSTRUMENTATION_KEY,\n    attach,\n    detach,\n    set_value,\n)\nfrom opentelemetry.sdk._metrics.metric_reader import MetricReader\nfrom opentelemetry.sdk._metrics.point import AggregationTemporality, Metric\nfrom opentelemetry.util._once import Once\n\n_logger = logging.getLogger(__name__)\n\n\nclass MetricExportResult(Enum):\n    SUCCESS = 0\n    FAILURE = 1\n\n\nclass MetricExporter(ABC):\n    \"\"\"Interface for exporting metrics.\n\n    Interface to be implemented by services that want to export metrics received\n    in their own format.\n    \"\"\"\n\n    @property\n    def preferred_temporality(self) -> AggregationTemporality:\n        return AggregationTemporality.CUMULATIVE\n\n    @abstractmethod\n    def export(self, metrics: Sequence[Metric]) -> \"MetricExportResult\":\n        \"\"\"Exports a batch of telemetry data.\n\n        Args:\n            metrics: The list of `opentelemetry.sdk._metrics.data.MetricData` objects to be exported\n\n        Returns:\n            The result of the export\n        \"\"\"\n\n    @abstractmethod\n    def shutdown(self) -> None:\n        \"\"\"Shuts down the exporter.\n\n        Called when the SDK is shut down.\n        \"\"\"\n\n\nclass ConsoleMetricExporter(MetricExporter):\n    \"\"\"Implementation of :class:`MetricExporter` that prints metrics to the\n    console.\n\n    This class can be used for diagnostic purposes. It prints the exported\n    metrics to the console STDOUT.\n    \"\"\"\n\n    def __init__(\n        self,\n        out: IO = stdout,\n        formatter: Callable[[Metric], str] = lambda metric: metric.to_json()\n        + linesep,\n    ):\n        self.out = out\n        self.formatter = formatter\n\n    def export(self, metrics: Sequence[Metric]) -> MetricExportResult:\n        for metric in metrics:\n            self.out.write(self.formatter(metric))\n        self.out.flush()\n        return MetricExportResult.SUCCESS\n\n    def shutdown(self) -> None:\n        pass\n\n\nclass PeriodicExportingMetricReader(MetricReader):\n    \"\"\"`PeriodicExportingMetricReader` is an implementation of `MetricReader`\n    that collects metrics based on a user-configurable time interval, and passes the\n    metrics to the configured exporter.\n    \"\"\"\n\n    def __init__(\n        self,\n        exporter: MetricExporter,\n        export_interval_millis: Optional[float] = None,\n        export_timeout_millis: Optional[float] = None,\n    ) -> None:\n        super().__init__(preferred_temporality=exporter.preferred_temporality)\n        self._exporter = exporter\n        if export_interval_millis is None:\n            try:\n                export_interval_millis = float(\n                    environ.get(\"OTEL_METRIC_EXPORT_INTERVAL\", 60000)\n                )\n            except ValueError:\n                _logger.warning(\n                    \"Found invalid value for export interval, using default\"\n                )\n                export_interval_millis = 60000\n        if export_timeout_millis is None:\n            try:\n                export_timeout_millis = float(\n                    environ.get(\"OTEL_METRIC_EXPORT_TIMEOUT\", 30000)\n                )\n            except ValueError:\n                _logger.warning(\n                    \"Found invalid value for export timeout, using default\"\n                )\n                export_timeout_millis = 30000\n        self._export_interval_millis = export_interval_millis\n        self._export_timeout_millis = export_timeout_millis\n        self._shutdown = False\n        self._shutdown_event = Event()\n        self._shutdown_once = Once()\n        self._daemon_thread = Thread(target=self._ticker, daemon=True)\n        self._daemon_thread.start()\n        if hasattr(os, \"register_at_fork\"):\n            os.register_at_fork(\n                after_in_child=self._at_fork_reinit\n            )  # pylint: disable=protected-access\n\n    def _at_fork_reinit(self):\n        self._daemon_thread = Thread(target=self._ticker, daemon=True)\n        self._daemon_thread.start()\n\n    def _ticker(self) -> None:\n        interval_secs = self._export_interval_millis / 1e3\n        while not self._shutdown_event.wait(interval_secs):\n            self.collect()\n        # one last collection below before shutting down completely\n        self.collect()\n\n    def _receive_metrics(self, metrics: Iterable[Metric]) -> None:\n        if metrics is None:\n            return\n        token = attach(set_value(_SUPPRESS_INSTRUMENTATION_KEY, True))\n        try:\n            self._exporter.export(metrics)\n        except Exception as e:  # pylint: disable=broad-except,invalid-name\n            _logger.exception(\"Exception while exporting metrics %s\", str(e))\n        detach(token)\n\n    def shutdown(self) -> bool:\n        def _shutdown():\n            self._shutdown = True\n\n        did_set = self._shutdown_once.do_once(_shutdown)\n        if not did_set:\n            _logger.warning(\"Can't shutdown multiple times\")\n            return False\n\n        self._shutdown_event.set()\n        self._daemon_thread.join()\n        self._exporter.shutdown()\n        return True\n", "path": "opentelemetry-sdk/src/opentelemetry/sdk/_metrics/export/__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\nimport logging\nimport os\nfrom abc import ABC, abstractmethod\nfrom enum import Enum\nfrom os import environ, linesep\nfrom sys import stdout\nfrom threading import Event, RLock, Thread\nfrom typing import IO, Callable, Iterable, List, Optional, Sequence\n\nfrom opentelemetry.context import (\n    _SUPPRESS_INSTRUMENTATION_KEY,\n    attach,\n    detach,\n    set_value,\n)\nfrom opentelemetry.sdk._metrics.metric_reader import MetricReader\nfrom opentelemetry.sdk._metrics.point import AggregationTemporality, Metric\nfrom opentelemetry.util._once import Once\n\n_logger = logging.getLogger(__name__)\n\n\nclass MetricExportResult(Enum):\n    SUCCESS = 0\n    FAILURE = 1\n\n\nclass MetricExporter(ABC):\n    \"\"\"Interface for exporting metrics.\n\n    Interface to be implemented by services that want to export metrics received\n    in their own format.\n    \"\"\"\n\n    @property\n    def preferred_temporality(self) -> AggregationTemporality:\n        return AggregationTemporality.CUMULATIVE\n\n    @abstractmethod\n    def export(self, metrics: Sequence[Metric]) -> \"MetricExportResult\":\n        \"\"\"Exports a batch of telemetry data.\n\n        Args:\n            metrics: The list of `opentelemetry.sdk._metrics.data.MetricData` objects to be exported\n\n        Returns:\n            The result of the export\n        \"\"\"\n\n    @abstractmethod\n    def shutdown(self) -> None:\n        \"\"\"Shuts down the exporter.\n\n        Called when the SDK is shut down.\n        \"\"\"\n\n\nclass ConsoleMetricExporter(MetricExporter):\n    \"\"\"Implementation of :class:`MetricExporter` that prints metrics to the\n    console.\n\n    This class can be used for diagnostic purposes. It prints the exported\n    metrics to the console STDOUT.\n    \"\"\"\n\n    def __init__(\n        self,\n        out: IO = stdout,\n        formatter: Callable[[Metric], str] = lambda metric: metric.to_json()\n        + linesep,\n    ):\n        self.out = out\n        self.formatter = formatter\n\n    def export(self, metrics: Sequence[Metric]) -> MetricExportResult:\n        for metric in metrics:\n            self.out.write(self.formatter(metric))\n        self.out.flush()\n        return MetricExportResult.SUCCESS\n\n    def shutdown(self) -> None:\n        pass\n\n\nclass InMemoryMetricReader(MetricReader):\n    \"\"\"Implementation of :class:`MetricReader` that returns its metrics from :func:`metrics`.\n\n    This is useful for e.g. unit tests.\n    \"\"\"\n\n    def __init__(\n        self,\n        preferred_temporality: AggregationTemporality = AggregationTemporality.CUMULATIVE,\n    ) -> None:\n        super().__init__(preferred_temporality=preferred_temporality)\n        self._lock = RLock()\n        self._metrics: List[Metric] = []\n\n    def get_metrics(self) -> List[Metric]:\n        \"\"\"Reads and returns current metrics from the SDK\"\"\"\n        with self._lock:\n            self.collect()\n            metrics = self._metrics\n            self._metrics = []\n        return metrics\n\n    def _receive_metrics(self, metrics: Iterable[Metric]):\n        with self._lock:\n            self._metrics = list(metrics)\n\n    def shutdown(self) -> bool:\n        return True\n\n\nclass PeriodicExportingMetricReader(MetricReader):\n    \"\"\"`PeriodicExportingMetricReader` is an implementation of `MetricReader`\n    that collects metrics based on a user-configurable time interval, and passes the\n    metrics to the configured exporter.\n    \"\"\"\n\n    def __init__(\n        self,\n        exporter: MetricExporter,\n        export_interval_millis: Optional[float] = None,\n        export_timeout_millis: Optional[float] = None,\n    ) -> None:\n        super().__init__(preferred_temporality=exporter.preferred_temporality)\n        self._exporter = exporter\n        if export_interval_millis is None:\n            try:\n                export_interval_millis = float(\n                    environ.get(\"OTEL_METRIC_EXPORT_INTERVAL\", 60000)\n                )\n            except ValueError:\n                _logger.warning(\n                    \"Found invalid value for export interval, using default\"\n                )\n                export_interval_millis = 60000\n        if export_timeout_millis is None:\n            try:\n                export_timeout_millis = float(\n                    environ.get(\"OTEL_METRIC_EXPORT_TIMEOUT\", 30000)\n                )\n            except ValueError:\n                _logger.warning(\n                    \"Found invalid value for export timeout, using default\"\n                )\n                export_timeout_millis = 30000\n        self._export_interval_millis = export_interval_millis\n        self._export_timeout_millis = export_timeout_millis\n        self._shutdown = False\n        self._shutdown_event = Event()\n        self._shutdown_once = Once()\n        self._daemon_thread = Thread(target=self._ticker, daemon=True)\n        self._daemon_thread.start()\n        if hasattr(os, \"register_at_fork\"):\n            os.register_at_fork(\n                after_in_child=self._at_fork_reinit\n            )  # pylint: disable=protected-access\n\n    def _at_fork_reinit(self):\n        self._daemon_thread = Thread(target=self._ticker, daemon=True)\n        self._daemon_thread.start()\n\n    def _ticker(self) -> None:\n        interval_secs = self._export_interval_millis / 1e3\n        while not self._shutdown_event.wait(interval_secs):\n            self.collect()\n        # one last collection below before shutting down completely\n        self.collect()\n\n    def _receive_metrics(self, metrics: Iterable[Metric]) -> None:\n        if metrics is None:\n            return\n        token = attach(set_value(_SUPPRESS_INSTRUMENTATION_KEY, True))\n        try:\n            self._exporter.export(metrics)\n        except Exception as e:  # pylint: disable=broad-except,invalid-name\n            _logger.exception(\"Exception while exporting metrics %s\", str(e))\n        detach(token)\n\n    def shutdown(self) -> bool:\n        def _shutdown():\n            self._shutdown = True\n\n        did_set = self._shutdown_once.do_once(_shutdown)\n        if not did_set:\n            _logger.warning(\"Can't shutdown multiple times\")\n            return False\n\n        self._shutdown_event.set()\n        self._daemon_thread.join()\n        self._exporter.shutdown()\n        return True\n", "path": "opentelemetry-sdk/src/opentelemetry/sdk/_metrics/export/__init__.py"}]}